In this article I will not talk about why in javascript you need promises, and in particular JQuery.Deferred. Also, I will not give background information, it is enough on the Internet. For example,
here or
here or
here .
This article is for techs who are already a little familiar with the Deferred object from the jQuery library, but have no experience writing complex chains (queues).
Training
All code examples discussed here use the asynchronous
$ .ajax () method, which returns the so-called jqXHR in which promis methods are implemented (done, fail, always, then). We will only need them, so we assume that $ .ajax returns a promise.
Some examples use the $ .map () and $ .each () methods that are part of the jQuery library.
Sequential execution
The simplest use of promises is the sequential execution of asynchronous operations. That is, the next operation does not begin until the current one ends.
')
$.ajax('http://echo.jsontest.com/id/1') .then(function(result){ console.log(JSON.stringify(result)); return $.ajax('http://echo.jsontest.com/id/2') }).then(function(result){ console.log(JSON.stringify(result)); return $.ajax('http://echo.jsontest.com/id/3') }).then(function(result){ console.log(JSON.stringify(result)); });
A live example is
here .
Parallel execution
Starts all asynchronous operations at the same time and proceeds to the next callback only when all parallel operations are completed.
$.when( $.ajax('http://echo.jsontest.com/id/1'), $.ajax('http://echo.jsontest.com/id/2'), $.ajax('http://echo.jsontest.com/id/3') ).then(function(result1, result2, result3){ console.log(JSON.stringify(result1[0])); console.log(JSON.stringify(result2[0])); console.log(JSON.stringify(result3[0])); })
A live example is
here .
Sequential parallel execution
Task: to execute one request, and after its completion, to start parallel execution of several more operations.
$.ajax('http://echo.jsontest.com/id/1') .then(function(result1){ console.log(JSON.stringify(result1)); return $.when( $.ajax('http://echo.jsontest.com/id/2'), $.ajax('http://echo.jsontest.com/id/3'), $.ajax('http://echo.jsontest.com/id/4') ) }).then(function(result2, result3, result4){ console.log(JSON.stringify(result2[0])); console.log(JSON.stringify(result3[0])); console.log(JSON.stringify(result4[0])); })
Living example .
Parallel execution of an unknown number of asynchronous operations
Moving on to more difficult situations. Suppose there is an array with links for each of which you need to make a parallel query. The task is complicated by the fact that the number of links in the array is not known in advance.
array = ['/url1', '/url2', ….. '/urlN']
We already know that the $ .when method is used for parallel start as follows:
$.when(promise1, promise2, … promiseN)
But, unfortunately, it is impossible to transfer an array of promises to this method. Therefore, this code can be rewritten a little, and then it is suitable for our task:
$.when.apply(this, [promise1, promise2, … promiseN])
But the complete solution of the problem:
urls = ['http://echo.jsontest.com/id/1', 'http://echo.jsontest.com/id/2', 'http://echo.jsontest.com/id/3'] promises = $.map(urls, function(url){ return $.ajax(url).then(function(result){ console.log(JSON.stringify(result)); }); }); $.when.apply(this, promises) .then(function(){ console.log('done'); });
The same code on
jsfiddle .
Sequential execution of an unknown number of asynchronous operations
The task is the same as in the previous example, but requests must be sent sequentially. This approach will help if there are a lot of requests, and the server part of the web application is not designed for such loads.
To solve this problem, we will “build up” the chain of promises in the cycle.
urls = ['http://echo.jsontest.com/id/1', 'http://echo.jsontest.com/id/2', 'http://echo.jsontest.com/id/3'] promise = $.when(); $.each(urls, function(index, url){ promise = promise.then(function(){ return $.ajax(url); }).then(function(result){ console.log(JSON.stringify(result)); }); }); promise.then(function(){ console.log('OK'); });
Here I applied a small trick: promise = $ .when (). Running the $ .when method with no arguments will return a resolved promise that will be the first link in the chain.
View code in action.
Simple error handling: one handler for all operations
For error handling, the .fail method is used. In the example below, this method is located at the very end of the promis chain, and if an error occurs, all the completed callbacks are skipped.
$.ajax('http://echo.jsontest.com/id/1') .then(function(){ console.log('OK 1'); return $.ajax('http://echo.jsontest.com/id/2'); }).then(function(){ console.log('OK 2'); return $.ajax('http://echo.jsontest_fail.com/id/3'); }).then(function(){ console.log('OK 3'); return $.ajax('http://echo.jsontest.com/id/4'); }).then(function(){ console.log('OK 4'); }).fail(function(){ console.log('error'); });
Run this code.
Stopping chain execution after error handling
If several error handlers (rejected promises) are used in the chain, then if any errors occur, all subsequent fail callbacks will be called. Example:
$.ajax('http://echo.jsontest.com/id/1') .then(function(){ console.log('OK 1'); return $.ajax('http://echo.jsontest.com/id/2'); }).then(function(){ console.log('OK 2'); return $.ajax('http://echo.jsontest_fail.com/id/3'); }).fail(function(){ console.log('error 1'); }).then(function(){ console.log('OK 3'); return $.ajax('http://echo.jsontest.com/id/4'); }).fail(function(){ console.log('error 2'); }).then(function(){ console.log('OK 4'); }).fail(function(){ console.log('error 3'); });
After executing this code, we will see the following in the console:
OK 1 OK 2 error 1 error 2 error 3
Link for those who do not believe.
But most likely this behavior is not useful to us. We will do this so that after processing the error, no subsequent callback will be called.
$.ajax('http://echo.jsontest_fail.com/id/1') .then(function(){ console.log('OK 1'); return $.ajax('http://echo.jsontest.com/id/2'); }, function(){ console.log('error 1'); return $.Deferred(); }).then(function(){ console.log('OK 2'); }, function(){ console.log('error 2'); })
Look at the
result .
In this example, two things should be noted. First, error handlers are now specified by the second argument of the .then method. Second, the error handler returns a Deferred object (promise) that is neither resolved nor rejected.
Continuing chain execution after error handling
Slightly changing the previous example can be done so that after processing the error the following done callback will be called.
$.ajax('http://echo.jsontest_fail.com/id/1') .then(function(){ console.log('OK 1'); return $.ajax('http://echo.jsontest.com/id/2'); }, function(){ console.log('error 1'); return $.when(); }).then(function(){ console.log('OK 2'); }, function(){ console.log('error 2'); })
This example differs from the previous one only in the seventh line.
Conclusion
The JQuery.Deferred component is not as complicated as it seems when you first meet it, but the other libraries that implement the functionality of promises are just as simple.
UPD
Thanks to
mayorovp for
comment