The existence of progress notifications doesn’t change the fact that, ultimately, every Promise is either resolved or rejected. (Or, it remains pending for eternity.) But why? Why not let Promises change to any state at any time?
Mainly, Promises are designed this way because programmers thrive on binary. We know exactly how to put 1s and 0s together to perform astounding feats of logic. That’s a big reason why Promises are so powerful; they let us treat tasks as booleans.
The most common use case for logically combining Promises is finding out when a set of async tasks has finished. Let’s say you’re showing a tutorial video while loading a game from the server. You want to start the game as soon as two things have happened, in any order.
The tutorial video has ended.
The game is loaded.
Given a Promise representing each of these processes, your task is to start the game when both Promises are resolved. How would you do that?
Enter jQuery’s when method.
| var gameReadying = $.when(tutorialPromise, gameLoadedPromise); |
| gameReadying.done(startGame); |
when acts as a logical AND for Promise resolution. The Promise it generates is resolved as soon as all of the given Promises are resolved, or it is rejected as soon as any one of the given Promises is rejected.
An excellent use case for when is combining multiple Ajax calls. If you need to make two POST calls at once and get a notification when both have succeeded, there’s no need to define a separate callback for each request.
| $.when($.post('/1', data1), $.post('/2', data2)) |
| .then(onPosted, onFailure); |
On success, when can get access to the callback arguments from each of its constituent Promises, but doing so is tricky. They’re passed as an argument list with the same order that the Promises were given to when. If a Promise provides multiple callback arguments, those arguments are converted to an array.
So, to get all of the callback arguments from all of the Promises given to $.when, you might write something like this (though I don’t recommend it):
| $.when(promise1, promise2) |
| .done(function(promise1Args, promise2Args) { |
| // ... |
| }); |
In this example, if promise1 resolved with the single argument ’complete’ and promise2 resolved with the arguments 1, 2, 3, then promise1Args would just be the string ’complete’, while promise2Args would be the array [1, 2, 3].
Although it’s possible, you shouldn’t parse when callback arguments if you don’t absolutely have to do so. Instead, attach callbacks directly to the Promises passed to when to collect their results.
| var serverData = {}; |
| var getting1 = $.get('/1') |
| .done(function(result) {serverData['1'] = result;}); |
| var getting2 = $.get('/2') |
| .done(function(result) {serverData['2'] = result;}); |
| $.when(getting1, getting2) |
| .done(function() { |
| // the GET information is now in serverData... |
| }); |
$.when, and other jQuery methods that take Promises, allow you to pass in non-Promises. These are treated like Promises that have resolved with the given value in the corresponding argument slot. For instance,
| $.when('foo') |
will give you a Promise that immediately resolves with the value ’foo’; the following
| var promise = $.Deferred().resolve('manchu'); |
| $.when('foo', promise) |
will give you a Promise that immediately resolves with the values ’foo’ and ’manchu’; and the following
| var promise = $.Deferred().resolve(1, 2, 3); |
| $.when('test', promise) |
will give you a Promise that immediately resolves with the values ’test’ and [1, 2, 3]. (Remember, when a Deferred passes multiple arguments to resolve, those arguments are coerced to an array by $.when.)
This raises the following question: how does $.when know whether an argument is a Promise? It turns out that jQuery checks each argument for a method named promise; if one exists, jQuery uses the value returned by that method. A Promise’s promise method simply returns itself.
As you’ll recall from Promises in the jQuery API, jQuery objects also have a promise method, which means that $.when “coerces” jQuery objects into their animation Promises. So, if we want to create a Promise that will resolve when we’ve fetched some data and the #loading animation has completed, all we have to do is write this:
| var fetching = $.get('/myData'); |
| $.when(fetching, $('#loading')); |
Just remember that we have to do this after starting the animation. If #loading’s animation queue is empty, its Promise resolves immediately.
18.224.67.58