3

One use of jQuery's Deferred system is with the $.when() function. It takes a variable number of Promise's and will do something when they allresolve(or when the firstreject`s.)

So if you wanted to operated on two Ajax JSON queries for instance you could do:

var funkyPromise = $.getJSON('http://funky.json.service.com');
var awesomePromise = $.getJSON('http://awesome.json.service.com');

$.when(funkyPromise, awesomePromise).then(function() {
  /* do something amazing with the two objects */
}, function() {
  /* at least one of the services failed this time */
});

Another thing you can do with jQuery's Deferred system is to make data pipelines by "chaining" the data from one Deferred through another using the pipe method before ultimately using it:

$.getJSON('http://funky.json.service.com')
  .pipe(funkytoFunkier);
}).done(function(funkyData) {
    /* we now have a funkier version of what the web service gave us */
});

Everything wonderfully asynchronous and decoupled.

But what happens if I want to use $.when() on two asynchronous Promises but we don't have one of them yet because it will come through a pipe asynchronously?

var funkyPromise = $.getJSON('http://funky.json.service.com');
var awesomePromise = $.getJSON('http://awesome.json.service.com');

// run "funky" through an asynchronous "pipe" to get "funkier"
//
// ... but ... how ??

$.when(funkyier, awesome).then(function() {
  /* do something amazing with the two objects */
}, function() {
  /* at least one of the services failed this time */
});

So what goes in the middle section?

  • Is it blindingly obvious and I just can't see it?
  • Is it possible but something quite subtle and tricky?
  • Would some new "inverted" equivalent of pipe() or $.when() etc make it easier?
hippietrail
  • 15,848
  • 18
  • 99
  • 158
  • Would it be acceptable to nest these deferred calls? – DrColossos Sep 05 '12 at 13:24
  • @DrColossos: For this the problem getting my head around it, so for some use cases nesting would be an option, there may also be use cases where it's not so I want to understand it without nesting... if it's possible that is (-: – hippietrail Sep 05 '12 at 15:46

2 Answers2

3

pipe() returns a new promise that will be resolved when the pipe is resolved, so you only have to write:

var funkyPromise = $.getJSON('http://funky.json.service.com');
var awesomePromise = $.getJSON('http://awesome.json.service.com');

$.when(funkyPromise.pipe(funkytoFunkier), awesomePromise).then(function() {
  /* do something amazing with the two objects */
}, function() {
  /* at least one of the services failed this time */
});

Note that, starting with jQuery 1.8, then() does the same thing as pipe(), so the two methods are basically interchangeable.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • Yes you're completely right. I'd been overthinking this for weeks actually (because I didn't fully grok all the Deferred / asynchronous / decoupling issues I suppose) but after writing it all down clearly then walking up and down the beach for a while mulling it over I came to the same conclusion that it's utterly trivial. Test code coming in an answer... – hippietrail Sep 05 '12 at 15:49
1

@Frédéric Hamidi knew the right answer but I had to figure it out (-: Here's a trivial example I got working after Frédéric wrote his answer but before I read it:

>> jsFiddle ...

function foo() {
    var time = Math.floor(Math.random() * 3000),
        d = $.Deferred();

    console.log('foo starting: ' + time);
    setTimeout(function() {
        console.log('foo resolving');
        d.resolve(time);
    }, time);

    return d.promise();
}

function bar() {
    var time = Math.floor(Math.random() * 500),
        d = $.Deferred();

    console.log('bar starting: ' + time);
    setTimeout(function() {
        console.log('bar resolving');
        d.resolve(time);
    }, time);

    return d.promise();
}

function baz(previousTime) {
    var time = Math.floor(Math.random() * 500),
        d = $.Deferred();

    console.log('baz starting: ' + time);
    setTimeout(function() {
        console.log('baz resolving');
        d.resolve(previousTime + ' + ' + time + ' = ' + (previousTime + time));
    }, time);

    return d.promise();
}

$.when(foo(), bar().pipe(baz))
    .then(function(foo, barBaz) {
        console.log('when OK', [foo, barBaz]);
    });​
Community
  • 1
  • 1
hippietrail
  • 15,848
  • 18
  • 99
  • 158