Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript closure "stores" value at the wrong time

I'm trying to have a counter increase gradually. The following works:

function _award(points){    
  var step = 1;
  while(points){
    var diff = Math.ceil(points / 10);
    setTimeout( "_change_score_by("+diff+");" /* sigh */,
                step * 25);
    points -= diff;
    step++;
  }
}

However, it uses an implicit eval. Evil! Let's use a closure instead, right?

function _award(points){    
  var step = 1;
  while(points){
    var diff = Math.ceil(points / 10);
    setTimeout( function(){ _change_score_by(diff); },
                step * 25);
    points -= diff;
    step++;
  }
}

Obviously, this doesn't work. All closures created catch the last value diff has had in the function -- 1. Hence, all anonymous functions will increase the counter by 1 and, for example, _award(100) will increase the score by 28 instead.

How can I do this properly?

like image 382
badp Avatar asked Oct 17 '10 15:10

badp


2 Answers

This is a known problem. But you can easily create a closure on each loop iteration:

(function(current_diff) {
    setTimeout(function() {_change_score_by(current_diff);},
               step * 25);
})(diff);
like image 96
Nikita Rybak Avatar answered Sep 29 '22 18:09

Nikita Rybak


Solving the Closure Loop Problem gets less messy with ECMAScript Fifth Edition's Function#bind method:

setTimeout(_change_score_by.bind(window, diff), step*25);

You can hack this feature into browsers that don't support it yet to get the benefit now.

like image 35
bobince Avatar answered Sep 29 '22 18:09

bobince