A Case for Deferred.chain

A Case for Deferred.chain

As of today, Deferreds are awesome to deal with asynchronous tasks. However, they lack a mean to filter/chain tasks without cumbersome code.

For instance, let's say you want to chain two ajax requests. Here is how you'd do it now:

$.ajaxurl ).done(functionresponse {
    $.ajaxreponse.url ).donecallback );
});


It gets a bit trickier when you need to give a promise for the end of the whole task in order to decouple the code and not have to provide the callback when the request is initiated (which is the crux of Deferred):

function chainedAjaxurl {
    return $.Deferred(functiondefer {
        $.ajaxurl ).done(functionresponse {
            $.ajaxreponse.url ).thendefer.resolvedefer.reject );
        }).faildefer.reject );
    }).promise();
}






You have to create a deferred to wait for the second ajax call and handle all the resolve and reject plumbery by hand... you can imagine how complex it could get with more and more chained requests.

With deferred.chain, here is how it would be coded:

function chainedAjaxurlcallback {
    return $.ajaxurl ).chain(functionresponse {
        return $.ajaxresponse.url );
    });
}




It's much more direct and to the point and if you need to chain more, just add chain statements and you'll get the proper promise returned.

Just like when, chain can also deal with non-observable values, so it can be used to filter responses. This comes in handy when dealing with ajax requests in a when statement:

function simpleAjaxurloptions {
    return $.ajaxurloptions ).chain(functionresponse {
        return response;
    });
}

$.whensimpleAjax(...)simpleAjax(...).done(functionresponse1response2{
    // response1 and response2 are no longer arrays






    // since the filtered promise is just the actual
    // ajax response, not the reponse with the jqXHR
    // object and the status like a normal ajax call
});


You can also use the filtering ability to deliver promises that are resolved with the data that is of interest to each part of the application:

var request $.ajaxurloptions ),
    status request.chain(functionresponse {
        return response.status;
    }),
    data request.chain(functionresponse {
        return response.data;
    });
    
status.done(functionresponseStatus {
    // Deal with the status
});

data.done(functionresponseData {
    // Deal with the data
});















This helps completely decouple portions of an app by hiding the actual data structure of the response from parts that don't need to know about it. It also helps refactoring: it's pretty easy now to change the logic into two separate ajax calls:

status $.ajaxurlStatusoptionsStatus ).chain(functionresponse {
    return response;
});

data $.ajaxurlDataoptionsData ).chain(functionresponse {
    return response;
});






Or even, to have dependencies between ajax calls:

status $.ajaxurlStatusoptions ).chain(functionresponse {
    return response;
});

data status.chain(functionstatus {
    return status === "OK" $.ajaxurlDataoptionsData null;
})





.chain(functionresponse {
    return response;
});

As you can see, Deferred.chain is a very useful method and it's actually pretty small too ;)