Search code examples
javascriptsettimeoutpass-by-reference

Why does setTimeout use the old version of my function?


When I run the following code:

let fn = () => console.log('original fn was called');
fn();
setTimeout(fn.bind(this), 250);
setTimeout(() => fn(), 500);
setTimeout(fn, 750);
fn = () => console.log('updated fn was called');

This is outputted:

original fn was called
original fn was called
updated fn was called
original fn was called

I intuitively grok why the first two lines are outputted:

  1. The function is immediately called.
  2. The bind method is producing a new function from fn, therefore 'locking' in it's behavior.

However, I'm confused as to why lines 3 and 4 are different. I suppose in the 3rd case, the reference to fn is not being evaluated until the wrapping anonymous function is called. So it makes sense that it would utilize the updated implementation of fn. But in the 4th case, why doesn't setTimeout make use of the latest version of fn?


Solution

  • A variable reference is evaluated only when the interpreter runs the statement. In 1, 2, and 4, you're referencing fn immediately, and using it somehow to pass to setTimeout:

    fn();
    setTimeout(fn.bind(this), 250);
    setTimeout(fn, 750);
    

    In 3, you're not referencing fn immediately - you're passing in a different anonymous function. Only when that anonymous function runs and the lines inside it get run do the references on those lines get checked by the interpreter to see what value the variable name points to.

    But in the 4th case, why doesn't setTimeout make use of the latest version of fn?

    Because you referenced fn directly when calling setTimeout, just like in cases 1 and 2.

    In the 3rd case, you have a function which contains a reference to fn, which will be evaluated when the anonymous function runs and the interpreter comes across that line - but not before.