Matthew
Matthew

Reputation: 11260

Javascript: function in setTimeout and references

I have an issue with a function call in setTimeout in a loop.
The parameters passed to the function are the last values computed in the loop for each iteration, please see example below.

for(var i=0; i<datesYM.length; ++i) {
    console.log(datesYM[i]);
    var dateYM = datesYM[i];
    setTimeout(function() {
        myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM)
    }, Math.floor(Math.random()*5001));
}

myDB.markMonthsValuesAsUpdated2 = function(myDoctorId, dateYM) {
    console.log(dateYM);
    [...]

Prints:

2012-01
2012-02
2012-03
2012-04
2012-05
2012-06
2012-07

2012-07
2012-07
2012-07
2012-07
2012-07
2012-07
2012-07

Upvotes: 2

Views: 296

Answers (2)

user1106925
user1106925

Reputation:

Create variable scope via a function that creates and returns the timeout handler.

function create_timeout_handler(myDoctorId, dateYM) {
    return function() {
        myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM)
    };
}

Then invoke that function and pass it whatever needs to be scoped.

setTimeout( create_timeout_handler(myDoctorId, dateYM), Math.floor(Math.random()*5001));

This is because every function you were giving to setTimeout was being created in the same variable scope, so they were all referencing the same dateYM variable, which was being overwritten in the loop.

Passing dateYM to another function that creates and returns the handler ensure that each handler is referencing the dateYM variable that was unique to each invocation of create_timeout_handler.

Upvotes: 1

Wayne
Wayne

Reputation: 60414

Wrap the body in its own self-executing function to force a new scope:

for(var i=0; i<datesYM.length; ++i) {
    console.log(datesYM[i]);
    var dateYM = datesYM[i];
    (function(dateYM) {
        setTimeout(function() {
            myDB.markMonthsValuesAsUpdated2(myDoctorId, dateYM)
        }, Math.floor(Math.random()*5001));
    })(dateYM);
}

Without this, every function created in the loop closes over the same instance of dateYM, which has the value of the last iteration by the time any of those functions execute. Since JavaScript has function scope, the wrapper function creates a new dateYM on each iteration, so that each new function passed to setTimeout has its own instance.

Upvotes: 5

Related Questions