0

I would like to know how to pass data by value, rather than by reference, into a then handler on a jQuery Deferred object.

I have the following example code to illustrate my question:

var value;
var deferred = new $.Deferred();
var data = ["A", "B", "C", "D"];

// add a separate call to the chain for each piece of data
for (var i = 0; i < data.length; i++) {
  value = data[i];
  deferred.then(function(response) {
    console.log(response+':'+value);
  });
}

deferred.resolve("test");

The result I want to get:

test:A
test:B
test:C
test:D

The result I actually get:

test:D
test:D
test:D
test:D

It seems the value of value is evaluated at the time the then handler is executed, whereas I want it to be evaluated at the time the then handler is queued.

I have a JSfiddle, hope someone can help?

Alex Bowyer
  • 681
  • 1
  • 6
  • 17

3 Answers3

2

Yes.. you have to create a closure around it

for (var i = 0; i < data.length; i++) {
    value = data[i];
    deferred.then( function(val) {
        return function(response) {
            console.log(response+':'+val);
        }}(value));
}
broc.seib
  • 21,643
  • 8
  • 63
  • 62
ilan berci
  • 3,883
  • 1
  • 16
  • 21
1

Try this:

var value;
var data = ["A", "B", "C", "D"];

// add a separate call to the chain for each piece of data
for (var i = 0; i < data.length; i++) {
  value = data[i];
  $.when($.Deferred().resolve("test"), $.Deferred().resolve(value))
  .then(function(response, x) {
      console.log(response + ':' + x);
  });
}

JsFiddle

0

Seems the simplest solution would be to move your for loop into the promise callback.

deferred.then(function(response) {
  for (var i = 0; i < data.length; i++) {
     value = data[i];
     console.log(response+':'+value);
  }
});

If you need to have the loop outside of your promise handling, I would consider not defining the function in a loop and using a curried function instead:

function processResponse(val){
   return function(response){
      console.log(response,':',val);
   }
}

for (var i = 0; i < data.length; i++) {
  value = data[i];
  deferred.then(processResponse(value));
}
Rob M.
  • 35,491
  • 6
  • 51
  • 50
  • That works in this simplified example, but in my real world case it's not so feasible. I'm looking for a way to do it but keeping the same nesting structure. – Alex Bowyer Apr 24 '15 at 17:36
  • Thanks for adding that alternative approach. Could you explain the "curried function", and how it works? – Alex Bowyer Apr 24 '15 at 17:45
  • 1
    Curried functions utilize a closure to maintain access to the parent function's scope and return a function that makes use of the "curried" data and whatever is passed to the returned function. I think Crockford does a good job of explaining currying in JS here: http://www.crockford.com/javascript/www_svendtofte_com/code/curried_javascript/index.html – Rob M. Apr 24 '15 at 17:55