jQuery, Promises & Deferred
Written by Ian Elliot   
Monday, 17 September 2012
Article Index
jQuery, Promises & Deferred
Chaining and Combining Promises

Promises are the way that JavaScript deals with tasks that take a long time to complete and it is important to understand how they work. jQuery implements and makes use of promises, but for reasons of compatibility does it in a way that might confuse things. As long as you approach it correctly, jQuery is a perfectly standard implementation of the promise object.

Asynchonous jQuery

What is the problem?

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 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. What you want to do is:

Task A
Task B - long running
Task C - processes the result of Task B
Task D - nothing to do with the result of Task B

But if you do the tasks in this way the everything hangs when you start task B because the UI thread isn't able to do anything else. To keep things responsive what you have to do is convert Task C into a callback and make Task B return before it is complete. The program now looks like:

Task A
Task B - long running but returns at once with callback C
Task D - nothing to do with the result of Task B

Task C - processes the result of Task B

The callback is now positioned out of the line of the natural flow of control. You think of the program as being do A, do B, do C and then do D, but you really don't know when C will be executed and most likely it will be after D.

Asynchronous programming is messy and it gets messier as you use more and more asynchronous calls, but you have to approach things in this way or accept that your application will come to a halt every now and again. In normal use there are lots of occasions when a long running process is unavoidable. For example when you use Ajax techniques to download a file you have to wait for a long time while the file downloads. If you simply make the program wait while the file downloads then the result is a completely frozen UI. This is just one example of many.

Asynchronous programming is unavoidable.

Promises are a way of trying to make asynchronous programming easier and all many jQuery functions make use of promise objects as an alternative to callbacks.

Let's look first at the basic operation of a promise.

The Basic Promise

A operation that will takes some time will generally return a promise object at once:

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.

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. The onError function is option and is called if an error occurs while the slow function is executing. The onProgress function is called periodically to indicate progress - not all promise objects support this. Also you only have to specify the onComplete function if you don't want to handle the error or monitor progress.

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  state. If the task ends with an error it enters the rejected state.

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. For example, if the slow function has returned at once, the promise object doesn't do anything until you use the then method to set the onComplete function which is then called at once.

The Ajax Get With a Promise

An example of using a promise in a function that is potentially slow is provided by the jQuery Ajax get method, but in fact any of the Ajax functions work in the same sort of way.

The get method will return any file as specified by the URL you provide. Of course, it could take some time to return the file as it has to be downloaded. This is a perfect use of the promise object and the get method returns a promise.

So. for example. if you want to download the file you might use:

$.get("TextFile.txt");

which would start the file downloading using another thread of execution and return immediately.

How do you get the contents of the file when the task completes?

The early version of jQuery didn't use promises and you had to supply call-back functions within the get method. Since jQuery 1.5 all of the Ajax functions have returned a promise object, which is much easier to use.

To download a file you now simply have to keep a reference to the returned promise object:

var myPromise = $.get("TextFile.txt");

and make use of its then method to process the returned data:

myPromise.then(function (value) {
                  console.log(value);
               });

You could also define functions to handle an error or report the progress of the download. In this case the anonymous function is called when the file has completed its download.

The first value returned by the promise is the contents of the file as a string.

One of the problems with trying to find out what functions return as their final result is that usually the documentation just gives the fact they return a promise object with no mention of what the values are.

In jQuery what is passed to an onComplete, onError or onProgress function is determined by the asynchronous function that returns the promise object. It can return multiple values. In the case of the Ajax methods it returns a set of values corresponding to the properties of the jqXHR object in the order they are documented.

Also, although you can write the code as above with the then method called later, it is usual to not store the promise object and simply call the then method as in:

$.get("TextFile.txt").then(
 function (value) {
  console.log(value);
 });

It is also worth remembering that the functions defined within a then method are closures and so have access to the variables that are in scope at the point at which the function is defined.

Promises in jQuery aren't quite a pure and clean as the previous example suggests.

In fact the get method doesn't return a promise object but a mix-in, a jqXHR object, that is part the original object that get used to return and part a promise object. This allows more flexibility than just returning a promise object and it is one of the strengths of JavaScript's approach to object-oriented programming - but it can be confusing.



Last Updated ( Tuesday, 12 February 2013 )
 
 

   
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.