13

Consider the following example:

var cb = function (t) {
    console.log('callback -->' + t);
};

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

Working example at jsfiddle

The output of this code snippet is:

0
1
2
callback ---> 3
callback ---> 3
callback ---> 3

Everything works as expected, for loop puts 3 callback calls into the event loop. By the end of the for loop i == 3 and when the callbacks get executed all of them print 3 because they contain the link to the i which is 3. How could this snippet be improved so when the callback gets executed it uses the actual value which was passed to it.

The output should be:

callback ---> 1
callback ---> 2
callback ---> 3

Thanks in advance.

4 Answers 4

18

Create a closure so that the setTimeout handler will refer to the closure's local variable (which in this case, we also named i), not the i from the loop:

for (var i = 0; i < 3; i++) {
    (function (i) {
        console.log(i);
        setTimeout(function () {
            cb(i);
        }, 1000);
    }(i));
}
Sign up to request clarification or add additional context in comments.

Comments

6

You could try .bind:

for(var i = 0; i<3; i++) {
    console.log(i);
    setTimeout(cb.bind(null, i),1000);
}

The demo.

The traditional way to handle this is to create a closure:

for(var i = 0; i<3; i++) {
    console.log(i);
    setTimeout((function(i){return function(){cb(i)}}(i)),1000);
}

1 Comment

Thanks a lot for the info on .bind I'll ponder over the documentation about it. Bind is ES5 only so I can't use it yet. Your closure solution works fine however Joseph posted similar solution first so I'm afraid I'll choose his answer. Nevertheless, thanks for sharing your knowledge !
3

A frequent question. Let's try to use some features of future of JS. I mean let. It creates local scope variable and you don't need to use a closure or another trick. But now it works only in FF (i'm using 20.0.1)

for(var i = 0; i<3; i++) {
    console.log(i);
    let a = i;
    setTimeout(function(){               
        cb(a);
    },1000);
}

Comments

1

I will just use setTimeout() in your cb's functoin

var cb = function (t) {
  setTimeout(function(){
    console.log('callback -->' + t);
  },1000);
};


for(var i = 0; i<3; i++) {
    console.log(i);
        cb(i);
}

2 Comments

Nice, but there's no need for cb to not be defined inline ;-)
Unfortunately this can't be done. Callback is called by the async function but it doesn't contain one itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.