JQuery Deferred usage examples

    In this article I will not talk about why javascript needs promises, and in particular JQuery.Deferred. I will also not provide reference information, it is enough on the Internet. For example here or here or here .
    This article is for tech who is already a little familiar with the Deferred object from the JQuery library, but has no experience writing complex chains (queues).

    Training

    All the code examples discussed here use the asynchronous method $ .ajax () , which returns the so-called jqXHR which implements promis methods (done, fail, always, then). We 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 start 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 living example here .

    Parallel execution

    It 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 living example 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

    We pass to more difficult situations. Suppose there is an array with links for each of which you need to make a parallel request. 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 parallel $ .when method is used as follows:

    $.when(promise1, promise2, … promiseN)
    

    But, unfortunately, you cannot pass an array of promises to this method. Therefore, this code will have to be rewritten a little, and then it is suitable for our task:

    $.when.apply(this, [promise1, promise2, … promiseN])
    

    And here is the complete solution to 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 jsfiddle code .

    Sequential execution of an unknown number of asynchronous operations

    The task is 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 side of the web application is not designed for such loads.
    To solve this problem, we will “increase” 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 little trick: promise = $ .when (). Running the $ .when method with no arguments will return the resolved promise, which 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 chain of promises and, when an error occurs, all done 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.

    Stop chaining after error handling

    If several error handlers (rejected promises) are used in the chain, then when an error occurs, 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 will not be useful to us. We make sure that after processing the error, no subsequent callback is 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 .
    There are two things to note in this example. First, error handlers are now set by the second argument to the .then method. Second - the error handler returns a Deferred object (promise) that is neither resolved nor rejected.

    Continuing chain execution after error handling

    By slightly changing the previous example, you can do so that after processing the error, the next 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 complex as it seems when you first met it, however, the rest of the libraries implementing the functionality of the promises are just as simple.

    UPD

    Thanks to mayorovp for comment

    Also popular now: