Page 1 of 4
Promises are a way of organizing asynchronous calls that is better than using callbacks. The callbacks are still there but they are come with a degree of organization. Previously jQuery was criticized for implementing a promise mechanism that was non-standard. Promises in jQuery 3 are compatible with the standard.
Events, Async & AJAX
Is now available as a print book: Amazon
- Events, Async & Ajax (Book Only)
- Reinventing Events
- Working With Events
- Asynchronous Code
- Consuming Promises
- Using Promises
- Ajax the Basics - get
- Ajax the Basics - post
- Ajax - Advanced Ajax To The Server
- Ajax - Advanced Ajax To The Client
- Ajax - Advanced Ajax Transports And JSONP
- Ajax - Advanced Ajax The jsXHR Object
- Ajax - Advanced Ajax Character Coding And Encoding
buy from Amazon
In this chapter we are going to look at using jQuery promises to create asynchronous code that is easy to understand and hard for bugs to hide in. Specifically we are going to look at how to use the promises that other functions return to use in place of callbacks. That is we are going to be looking at consuming promises.
In the next chapter we will look at how to add promise support to your own asynchronous code i.e. how to produce promises for others to use. This involves making use of another type of object - a deferred which has all of the properties and methods of a promise and a few more.
jQuery was a pioneer of promises and this was good but now means that its approach is slightly non-standard. It was a lot non-standard before jQuery 3 but now it works according to the standard with some extras and omissions.
What is the problem?
The usual solution to this problem as explained in earlier chapters is to use a callback function. The callback is passed to the function that is going to do the long job and instead of keeping the thread waiting it simply returns immediately. This allows the thread to do other work while it get on with its task. When it has finished it calls the callback function, usually with the result of the task. The callback function then processes the results.
Callbacks are difficult because they alter the flow of control in an unnatural way and this has been explained in earlier chapter. However it is worth saying that the precise problem that promises were introduced to solve is that of running asynchronous tasks one after the other. That is if you have three asynchronous tasks and simply call them:
Then they will execute in an order that depends on how long each takes. They effectively run in parallel. If you want them to run sequentially - that is TaskB only starts after TaskA ends, and TaskC starts after TaskB ends then you have to use some sort of mechanism to signal the end of each task and initiate the next one in the sequence.
The callback solution is to use nested callbacks. Something like:
where each task accepts a callback that is invoked when it ends. This looks simple enough in this example but this is because it is over simplified. In real life nested callbacks quickly degenerate into "callback hell" and there is no standard way of handling errors accept for having a success and a failure callback for each function.
Running sequential tasks is something promises make easy.
This is also the problem that jQuery's functions queues solve and this is explained in chapter 3. Promises are a more general approach to the whole problem of working with asynchronous functions and as such they are worth knowing about.
Let's look first at the basic operation of a promise.
The Basic Promise
A operation that takes some time will generally return a promise object at once and then complete its task in the background:
Notice that even though you get a promise object returned at once it is unlikely that slowFun has finished whatever it is trying to do.
There is also the idea that the slow function is computing a value which your program is waiting for - this isn't necessarily the case as you could be just waiting for it to complete what it is doing. However in most cases the promise is regarded as being a promise to deliver a future value and this view is often helpful.
What do you do with a promise?
The most common thing to do is to use its then method to setup what should happen when the slow function is complete.
where onComplete is a function that is called when the slow function finished its task. It is passed the value, or in jQuery's case a set of values, that the slow function generates on completion. That is when the promise is fulfilled it supplies the value to the onComplete. The onError function is optional and is called if an error occurs while the slow function is executing. The value that the onError receives is the reason for the error. The onProgress function is called periodically to indicate progress - not all promise objects support this.
That is you only have to specify the onComplete function if you don't want to handle the error or monitor progress that is onError and onProgress are optional.
A promise object is in one of three states.
- When it is first created it is pending.
- If the task ends correctly then it is in the resolved or fulfilled state.
- If the task ends with an error it enters the rejected state.
A promise is also said to be settled if it isn't pending i.e. if it is either fulfilled or rejected. Once a promise is settled it's state and value doesn't change.
It is important to realize that there is no rush to use the then method, or any other method to define the functions to be called. The promise object's state determines if and when any of the functions are called. If a promise is already settled when you add functions as part of a then they will still be carried out. The key idea is that a promise always activates the onComplete, onError or onProgress after the code that is currently running has finished.
That is a promise always executes its functions asynchronously.
You can add as many onComplete, onError or onProgress functions as you want to. For example:
When the promise is settled then the handlers are called in the order that they ware added.