3

Recently I asked about how I could trigger a variable number of jquery get() requests and I received this code as a really efficient working solution:

var d = new $.Deferred();
var promise = d.promise();
var results = [];
for (var i = 0; i < 5; i++) {
    (function(i) {
        promise = promise.then(function() {
            return $.ajax('/echo/json/', {
                data: {
                    json: JSON.stringify({foo: i})
                }
            }).done(function(data) {
                results[i] = data;
            });
        });
    }(i));
}

promise.done(function() {
    console.log(results);
});

d.resolve(); 

fiddle

I'm looking for an explanation of how exactly this works with the multiple done()s attached to each promise.then(), and then the final done() attached to the promise itself. Is it that a promise (from what I understand is a special type of sort of deferred itself) can have a done, then the deferred itself can also have a done? If it weren't in a loop, what would this syntax look like for, say, three ajax requests in a row? Just trying to get my head around it. Thanks!

Community
  • 1
  • 1
1252748
  • 14,597
  • 32
  • 109
  • 229

2 Answers2

7

If you have an array of deferred objects you can actually deal with them using when and apply to get the promises and then use $.done() on them:

$.when.apply($, deferredArray).done(function() {
   for (var i = 0; i < arguments.length; i++) {
      //arguments[i] is the return of the singular done() statement    
   }
});

This works rather well when you don't know how many deferred objects you are working with.

danielrsmith
  • 4,050
  • 3
  • 26
  • 32
  • I received an answer similar to this in the first post, but I really have no idea what is going on with `apply`. How is it being used? What's the `$` as the first parameter? Thank you – 1252748 Feb 21 '13 at 21:55
  • actually in the answer, they used `null` as the first parameter: `$.when.apply(null, dfdArr).done(function () { for (response in arguments) { html_str += arguments[response][0];` – 1252748 Feb 21 '13 at 21:56
  • and where does `arguments` come from? what generates that? – 1252748 Feb 21 '13 at 21:57
  • 1. The apply() function is allowing us to pass every element in the deferredArray to the `when` of sorts. 2. `arguments` is part of the baseline javascript https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments – danielrsmith Feb 21 '13 at 22:02
  • The whole thing sort of reads in English "when all of the promises in the deferred array are done, do this..." – danielrsmith Feb 21 '13 at 22:03
3

A promise is a wrapper for the deferred that hides the resolve and reject methods, kind of the public interface of the deferred. For then() and done() it makes no difference if you use them on a deferred or on its promise. I'll use the term deferred for both from now.

That being said, done() returns the deferred itself which allows chaining. You can add as many done (and fail) handlers as you want like that. They will be called one by one.

then() returns a new deferred*, which can have its own done and fail handlers.

Note that in your code you replace the deferred in the promise variable on each loop iteration, so the final done() is not called on the original deferred but on the latest, returned by the last then().

All calls chained in one line would look like that (each then() returns a new promise. I marked, on which the done() methods are called):

d.then().done().then().done().then().done().then().done().done();
         ^^^^^^        ^^^^^^        ^^^^^^        ^^^^^^^^^^^^^
        promise 1     promise 2     promise 3       promise 4

*) actually its promise

Fabian Schmengler
  • 24,155
  • 9
  • 79
  • 111
  • Thank you for this explanation. I'm a bit confused by this: __"Note that in your code you replace the deferred in the promise variable on each loop iteration"__ `promise = promise.then` seems not so much like a replacement, but a concatenation. Is this incorrect? – 1252748 Feb 21 '13 at 21:59
  • Incorrect. I believe you code a lot of PHP? In JavaScript the dot is not a concatenation operator, it's like `->` in PHP ;-) – Fabian Schmengler Feb 21 '13 at 22:01
  • haha, yeah. Okay. Can you explain the last line that says "*) actually it's promise". – 1252748 Feb 22 '13 at 00:22
  • footnote explained: `then()` returns the promise of a new deferred object (**its**, not it's), not the deferred itself. But as I said, that makes no difference here. – Fabian Schmengler Feb 22 '13 at 05:41
  • 1
    "A promise is a wrapper for the deferred ...". Personally, I would find it confusing to have a Promise described as a "wrapper"; this doesn't conform to my mental model. For me, a better statement would be "a Promise is derived from a Deferred and exposes all of that Deferred's methods except those that resolve and reject". But if "wrapper" works for you (and anyone else), then I can't really say it's wrong. – Beetroot-Beetroot Feb 24 '13 at 10:51