3

I know that one of the ways to log 0 to 9 with this code:

EDIT: Source

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
      console.log(i);
    }), 10)
}

jsfiddle

is to make setTimeout self invoking and pass i as a parameter, like so:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i) {
      console.log(i);
    })(i), 10)
}

but I've tested making setTImeout self invoking without passing i, and it still works:

for(var i = 0; i < 10; i++) {
    setTimeout((function() {
      console.log(i);
    })(), 10)
}

jsfiddle

My questions:

  1. Why does it work even without passing i as a parameter?
  2. Is it necessary to pass i?
Jaeeun Lee
  • 3,056
  • 11
  • 40
  • 60
  • i doubt you are creating closure – brk Jul 20 '16 at 03:12
  • 3
    You could equally have written `console.log(i); setTimeout(undefined, 10);`. So what? It doesn't work. – Bergi Jul 20 '16 at 03:13
  • @user2181397 I've edited to clarify – Jaeeun Lee Jul 20 '16 at 03:17
  • @Bergi Not sure what you mean? – Jaeeun Lee Jul 20 '16 at 03:17
  • 1
    Well, you have two scopes: the for loop, and the setTimeout. The loop has the variable i, so you don't need to pass i to the setTimeout; its already referenced. – TheGenie OfTruth Jul 20 '16 at 03:17
  • @TheGenieOfTruth I added a link to the source. Do you mean the source is misinformed? – Jaeeun Lee Jul 20 '16 at 03:19
  • 1
    @JaeeunLee I mean that none of your two snippets actually creates a timeout or logs values asynchronously. – Bergi Jul 20 '16 at 03:20
  • @Bergi They do work as intended. Added fiddles. – Jaeeun Lee Jul 20 '16 at 03:22
  • 2
    @JaeeunLee: Thanks for adding the source of the snippet. Unfortunately, it's wrong! Have a look at [our canonical question](http://stackoverflow.com/q/750486/1048572) for a better reference (and maybe also [here](https://stackoverflow.com/questions/25266904/javascript-closure-not-working)). – Bergi Jul 20 '16 at 03:23
  • 1
    Check this [**JSFIDDLE**](https://jsfiddle.net/uqcterj3/) – brk Jul 20 '16 at 03:24
  • 1
    @JaeeunLee: Add a `console.log("now")` after the end of the loop. You'd expect to see it before the logs from the timeouts, wouldn't you? – Bergi Jul 20 '16 at 03:24

2 Answers2

8

Problem

It's not a Closure.

for(var i = 0; i < 10; i++) {
  setTimeout((function(i) {
    console.log(i);
  })(i), 10)
}

setTimeout expects actually a function as parameter, but you made it invoking immediately. So it logs immediately the value of i, without waiting. setTimeout has now the return of your anonymous function as first parameter which is undefined.

The same here:

for(var i = 0; i < 10; i++) {
   setTimeout((function() {
     console.log(i);
   })(), 10)
}

It's executed immediately, it doesn't wait for the 10ms. The only difference you didn't passed i as parameter, but it will look in the parent scope for a variable called i – and there is one.

You will see that your anonymous function is called immediately if you set the time to e.g. one second (1000).

Solution

A real closure would look like this:

Without parameter: You will see 10 times 10, because at time of execution of the inner function the loop is already finished which means i equals 10 at that time:

for(var i = 0; i < 10; i++) {
   (function() {
        setTimeout(function() {
         console.log(i);
      },10);
   })();
}

With parameter – you will get the expected result:

for(var i = 0; i < 10; i++) {
   (function(i) {
        setTimeout(function() {
         console.log(i);
      },10);
   })(i);
}
Community
  • 1
  • 1
ScientiaEtVeritas
  • 5,158
  • 4
  • 41
  • 59
  • Thanks! It would be great if you could also add how to make it work as it should. – Jaeeun Lee Jul 20 '16 at 03:31
  • Is the Solution #1 still a closure when it doesn't capture the value of `i` at the time of its creation? Or does it? – Jaeeun Lee Jul 20 '16 at 03:47
  • 1
    Actually the nature of a closure is to save the environment, save some variables and values like in the second example. So the definition of a closure is more than just an inner function. So if you're strict, I would say no, the first example isn't. – ScientiaEtVeritas Jul 20 '16 at 03:56
  • The first example is mostly used if you just want to create a new scope. Otherwise it doesn't make a difference if the setTimeout is inside of that function or directly in that for loop, the output is the same. They are called Immediately-Invoked Function Expression (IIFE). – ScientiaEtVeritas Jul 20 '16 at 04:01
3

In all your avobe code, you are executing the function given as the first parameter in setTimeout without returning anything. So what happens is you are returning undefined.

In the code below, I am also self-invoking, but returning a function for setTimeout to execute after a specific intervali * 10.

This is the closure function. Check the console logs.It prints the time stamp in miliseconds. There is a gap of 10ms.

for (var i = 0; i < 10; i++) {
  setTimeout((function(i) {
    return function() {
      console.log(i, performance.now() + 'ms');
    }
  })(i), i * 10)
}

Additional Info In the comment section, I have a query

what's the point, when you could just put the setTimeout inside an anonymous function? So I guess this is the code you are suggesting.

for (var i = 0; i < 10; i++) {
    setTimeout(function() {
      console.log(i, performance.now() + 'ms');
    }, i * 10)
}

If you run this snippet, you will get a log of 10 for every log. This is the beauty of closures. When you are executing the annonymous function

function() {
   console.log(i, performance.now() + 'ms');
}

It cant find i in current scope and searches it parent scope. And voila, it finds i, but unfortunately the loop has completed its iteration by that time and its value = 10 now. So logged 10 every time.

This is why we need closure.

Ayan
  • 2,300
  • 1
  • 13
  • 28
  • Sure, but what's the point, when you could just put the `setTimeout` inside an anonymous function? –  Jul 20 '16 at 03:29
  • @torazaburo Buddy I have update my answer to accomodate your explaination. Please let me know if this is the thing you were refering or I am missing something. – Ayan Jul 20 '16 at 03:40