Jay
Jay

Reputation: 634

callback scope during iteration

I know there are a ton of questions regarding callbacks, scope, and closures. I apologize in advance if this is a duplicate.

I have an each loop that calls a function that performs several async actions and then issues a callback. I'm losing scope (obviously) when the callback fires. What I've done is pass the item in the loop to the function and then I return it in the callback so I have the scope I need. Is that the best way to do this? I'm not looking for something overly complicated. I just want to make sure I'm not going to run into any "gotchas".

function doSomething(varA, varB, self, callback) {
  // do a lot of ajax stuff
  callback(varA + varB, self);
}

$.each( $('.selected'), function(i, item) {
  doSomething('a', 'b', item, function(retVal, self) {
    // process retVal and self
  }
}

Upvotes: 1

Views: 212

Answers (4)

gilly3
gilly3

Reputation: 91587

Since callback is defined within the .each() function, item still is in scope by the time you get to your callback function. So, if doSomething() never uses self, you don't need to pass it. You can just reference item:

function doSomething(varA, varB, callback) {  
  // do a lot of ajax stuff  
  callback(varA + varB);  
}  

$('.selected').each(function(i, item) {  
  doSomething('a', 'b', function(retVal) {  
    // process retVal and item
  });
});

Now, if the callback were defined outside of .each(), you'd have to do it the way you have it, passing item to doSomething():

function doSomething(varA, varB, self, callback) {  
  // do a lot of ajax stuff  
  callback(varA + varB, self);  
}  
function doSomethingCallback(retVal, self) {  
  // process retVal and item
}

$('.selected').each(function(i, item) {  
  doSomething('a', 'b', item, doSomethingCallback);
});

Upvotes: 0

JaredPar
JaredPar

Reputation: 755259

The main "gotcha" people run into with ajax and loops is trying to reuse the iteration variable inside the function which executes later. For example

for (var i in col) {
  $.ajax({
    url: '...' + i + '.html',
    success: function() {
      console.log(i);  // Why is i different???
    }
  });
}

This "gotcha" occurs because there is 1 instance of i shared amongst all of the success callbacks.

In your scenario though you have one i for every "iteration" level. So this gotcha won't hit you.

Upvotes: 0

Gustav Barkefors
Gustav Barkefors

Reputation: 5086

If you don't need the element reference inside doSomething, you can create a closure in this, slightly tidier way:

function doSomething(varA, varB, callback) {
  // do a lot of ajax stuff
  callback(varA + varB);
}

$.each( $('.selected'), function() {
  var self = this;
  doSomething('a', 'b', function(retVal) {
    // process retVal and self
  }
});

Upvotes: 1

Matt Ball
Matt Ball

Reputation: 359966

What you've got looks fine but for one thing: using $.each() instead of .each(). Try this:

$('.selected').each(function(i, item) {
  doSomething('a', 'b', item, function(retVal, self) {
    // process retVal and self
  }
}

Upvotes: 2

Related Questions