Deferred and promises are a very powerful tool for handling asynchronous events. In this blog post I will explain what they are and when to use them.
Let’s create gretting cards
As an example let’s say that we are building a UI for a greeting cards making application. Our UI may look something like this:
The user can select an animation, select the music and then click next. Our event flow will look like this:
In this first case we know everything we need from the beginning. When the user clicks ‘next’ we know which animation and which music to load. After we are done loading these assets we will show the greeting card. To do this is code we have several options:
We can count the assets already loaded:
1 2 3 4 5 6 7 8 9 |
|
This code simply keeps a count of assets loaded and when all are loaded it shows the card.
We can use a library like Async:
1 2 3 4 |
|
We simply tell the library that we want to load these two assets in parallel and then call ‘show’ when done. The library takes care of all the details for us. A library like this is great but we need to know everything that we need to load at the start.
Not knowing everything from the beginning
Now let’s imagine that we don’t want a ‘Next’ button anymore in our UI:
Here we just want to show the greeting card automatically after the user has selected the animation and the music. Maybe not the best user experience but it works for our example. We don’t know the order in which the user will select the assets.
If we want to stick with the previous way of doing thing (knowing everything at the start). Our event flow will looks something like this:
In the above flow we are waiting idle while the user is busy selecting the music. We don’t want this, we want to take advantage of this time to load the assets the user has already chosen. So our event flow should look more like this:
In this flow we start loading the animation as soon as the user has selected it. While the user is busy selecting the music the animation is loading in the background. As soon as the user select the music we start loading it too in paralell.
A library like as Async is not useful in this case anymore. We can however still count like before or we could use conditional like:
1 2 3 4 5 6 7 8 9 10 11 |
|
This works but it is not very elegant and becomes hard to maintain quickly.
Deferreds to the rescue
Here is where Deferreds shine. But let me explain what they are first. In a nutshell a Deferred is contract for an event that will happen in the future. Easier to explain this with some code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
We create an Deferred object that accepts listeners like ‘done’. At some point in our application we set this deferreds as done (‘resolve’). This will trigger all the listeners.
There are many Deferred implementations like jQuery (1.5+), underscore deferreds, promised-IO. My examples are using jQuery but the concepts are pretty much the same for all of them.
Aggregation
A deferred can also be aggregated (I will explain promises later):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
In this case when def1 and def2 are resolved the listener in the combined Promise will trigger.
So going back to our greeting cards example. To do this:
We can simply code it like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
No conditions, no counting. Quite elegant if you ask me.
What if it is already resolved?
Deferreds have another neat trick. Let’s say that the user selects the music first and it completely loads before we even start loading the animation.
By the time we add our aggregated listener the Deferred for the music has already been resolved:
1 2 3 4 5 6 7 8 9 10 |
|
No problems! The aggregated listener will still triggers, it knows that the Deferred is already resolved and acts as expected. This is something you cannot with common event listeners!
Fail and reject
Deferred can also be rejected as well:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
This gives us a way of handling errors and providing fallbacks.
Promises
A promise is mostly like a Deferred but it doesn’t provide the methods to resolve and reject it. This is useful when you want to give a reference to the Deferred to another object so it can add listeners but you don’t want to give that object the power to resolve the Deferred.
Let’s say you have a caller object with code like this:
1 2 3 4 5 6 7 8 9 10 11 |
|
This caller receives a Promise from the loader object, it can add listeners to the Promise or aggregate it with other Promises. But it cannot resolve or reject them. Trying to do something like:
promise.resolve();
will fail.
The code in the loader object will look something like this:
1 2 3 4 5 6 7 8 9 |
|
Note the def.promise() method which creates the promise. The jQuery ajax methods does exactly this, it gives you a promise back when called.
You can combine promises to you heart content:
1 2 3 4 5 6 |
|
Using Deferreds you can easily code something like this. Where you have many actions happening at the same time, each without a clear start and ending and depending on each other.
Conclusion
In conclusion Deferreds are best suited in situations where:
- You have several actions happening at the same time e.g. loaders
- You don’t know where the action starts and when it finishes e.g. user interaction
- You have other actions that depend on the completion of multiple other actions