Question

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?
Was it helpful?

Solution

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.

OTHER TIPS

@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]);
    });​
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top