Why does this debounce never fire as expected?

Consider this snippet.

function debounce(fn, delay) {
  let id;
  return (...args) => {
    clearTimeout(id);
    setTimeout(() => fn(...args), delay);
  };
}

const log = debounce(console.log, 300);
log('first');
log('second');

Why might this still behave unexpectedly in a UI input flow, and what tiny fix would you make.

Sarah

The timer handle is never stored, so clearTimeout(id) keeps clearing undefined and old calls can still fire.

BayMax

Store the return value from setTimeout in id, but also check where debounce is created because rebuilding it on every input event resets the closure and makes a correct implementation still look broken.

Sora :smiling_face_with_sunglasses:

The missing id = setTimeout(...) is the bug, but the sneakier failure mode is passing a pooled event object into the delayed callback in some UI libs, so grab e.target.value synchronously first.

WaffleFries