Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing parameters into a closure for setTimeout

I've run into an issue where my app lives in an iframe and it's being called from an external domain. IE9 won't fire the load event when the iframe loads properly so I think I'm stuck using setTimeout to poll the page.

Anyway, I want to see what duration is generally needed for my setTimeout to complete, so I wanted to be able to log the delay the setTimeout fires from my callback, but I'm not sure how to pass that context into it so I can log it.

App.readyIE9 = function() {
  var timings = [1,250,500,750,1000,1500,2000,3000];    
  for(var i = 0; i < timings.length; i++) {
    var func = function() {
    if(App.ready_loaded) return;
      console.log(timings[i]);
      App.readyCallBack();
    };
    setTimeout(func,timings[i]);
  }
};

I keep getting LOG: undefined in IE9's console.

What's the proper method to accomplish this?

Thanks

like image 834
user126715 Avatar asked Jun 01 '12 18:06

user126715


People also ask

Does setTimeout use closure?

The real question is how does each setTimeout function have access to the i variable? The answer is, you guessed it, closures. In the first example, I explained how a closure allows an inner function to reference an outer function's variables, even though the outer function no longer exists.

How many parameters does a setTimeout f unction accept?

Developers generally pass only two parameters to setTimeout method — the callback function and the timeout period.


2 Answers

This is happening because you are not closing around the value of i in your func. When the loop is done, i is 8 (timings.length), which doesn't exist in the array.

You need to do something like this:

App.readyIE9 = function() {
  var timings = [1,250,500,750,1000,1500,2000,3000];    
  for(var i = 0; i < timings.length; i++) {
    var func = function(x) {
      return function(){
          if(App.ready_loaded) return;
          console.log(timings[x]);
          App.readyCallBack();
      };
    };
    setTimeout(func(i),timings[i]);
  }
};
like image 99
Rocket Hazmat Avatar answered Oct 18 '22 04:10

Rocket Hazmat


When your function gets called by setTimeout sometime in the future, the value of i has already been incremented to the end of it's range by the for loop so console.log(timings[i]); reports undefined.

To use i in that function, you need to capture it in a function closure. There are several ways to do that. I would suggest using a self-executing function to capture the value of i like this:

App.readyIE9 = function() {
  var timings = [1,250,500,750,1000,1500,2000,3000];    
  for(var i = 0; i < timings.length; i++) {
    (function(index) {
        setTimeout(function() {
            if(App.ready_loaded) return;
            console.log(timings[index]);
            App.readyCallBack();
        }, timings[index]);
    })(i);
  }
};

As a bit of explanation for who this works: i is passed to the self-executing function as the first argument to that function. That first argument is named index and gets frozen with each invocation of the self-executing function so the for loop won't cause it to change before the setTimeout callback is executed. So, referencing index inside of the self-executing function will get the correct value of the array index for each setTimeout callback.

like image 35
jfriend00 Avatar answered Oct 18 '22 04:10

jfriend00