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.
Just jQuery Events, Async & AJAX
Is now available as a print book: Amazon
Contents
- Events, Async & Ajax (Book Only)
- Reinventing Events
- Working With Events
- Asynchronous Code
- Consuming Promises
- Using Promises
- WebWorkers
- 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
Also Available:
buy from Amazon
Advanced Attributes
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 v JavaScript Promises
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.
Some people say that now promises are a part of JavaScript you should simply ignore jQuery's promise library. However this isn't entirely possible if you want to make use of jQuery functions that return jQuery promises.
You can always convert a jQuery promise into a JavaScript promise using:
var javaScriptPromise=Promise.resolve(jQueryPromise);
However in most cases, as jQuery 3 promises work in the same way as a javaScript Promise there seems to be little reason to do this. My advice is to work with jQuery promises unless you really need to use some feature of a JavaScript promise that isn't available in jQuery.
At some point in the future it seems likely that jQuery will remove its version of promises and convert to using JavaScript promises and this should be possible without breaking your existing code.
In the rest of this chapter the focus will be on using jQuery promises and the few differences with JavaScript promises will be made clear. For most of the time you can forget that jQuery promises are anything different.
It is also worth mentioning that JavaScript standard promises are "thenables" which is essentially any object that has a then method. A thenable can be converted into a standard promise in the same way as a jQuery promise can be.
What is the problem?
As should be perfectly clear by now the problem is that JavaScript is single-threaded and this means if you were to call any functions that need to wait while some external event occurs - a file to download say - then, for the duration of the event, your app would appear to freeze. The user interface, and events in particular, are all handled by the same thread and if that thread is in a wait state then nothing else gets done.
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:
TaskA(); TaskB(); TaskC();
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:
TaskA(callBackTaskB(callBackTaskC)));
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:
var mypromise=slowFun(args);
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.
mypromise.then(onComplete,onError,onProgress);
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:
mypromise.then(onComplete1,onError1,onProgress1); mypromise.then(onComplete2,onError2,onProgress2);
When the promise is settled then the handlers are called in the order that they ware added.
|