Deferred for Javascript (Prototype)

    Prototype and twisted
    Continuing the Deferred theme for JavaScript, I suggest another rewriting of Deferred, now in Prototype terms . You can read more about Deferred itself in two of my past notes: Asynchronous programming: the Deferred concept and Deferred: all the details . In short, the most common and useful application of Deferred in JavaScript is working with AJAX or other RPC-over-HTTP calls, when you need to make a chain of logically connected calls, correctly handle errors that occur, etc. In my opinion, Deferred is essential in such situations.

    Let's move on to the examples: accessing some JSON-RPC API based on Prototype's Ajax.Request can be wrapped in Deferred as follows:

    var Api = Class.create({
        initialize: function(url)
        {
            this.url = url;
        },
        call: function(method, params)
        {
            var requestBody = $H({ 'method' : method, 'params' : params }).toJSON();
            var d = new Deferred();
            var onSuccess = function(transport)
            {
                result = transport.responseText.evalJSON();
                if ('faultCode' in result && 'faultString' in result)
                {
                    var err = new Error(result.faultString);
                    err.faultCode = result.faultCode;
                    err.faultString = result.faultString;
                    d.errback(err);
                }
                else
                {
                    result = result[0];
                    console.log("Got result: ", result);
                    d.callback(result);
                }
            };
            var onFailure = function(transport)
            {
                d.errback(new Error("API transport error: " + transport.status))
            };
            var onException = function(error)
            {
                d.errback(error);
            }
            new Ajax.Request(this.url, {
                    method: 'post',
                    postBody: requestBody,
                    requestHeaders: { 'Content-Type' : 'application/json' },
                    onSuccess: onSuccess,
                    onFailure: onFailure,
                    onException: onException,
                });
            return d;
        },
    });
    

    Here, any call to Api.callwill be returned by a new Deferred, which will contain the result of a remote call or exception (transport or from the server we are accessing). Let there be RPC calls sumand mult, which, respectively, add and multiply their arguments. Then evaluating the expression (2+3)*7using our class Apiwill look like this:

      var api = new Api;
      api.call('sum', [2, 3])
         .addCallback(
                   function (sum_of_2_and_3) 
                   { 
                         return api.call('mult', [sum_of_2_and_3, 7]); 
                   })
         .addCallback(
                   function(result)
                   { 
                         alert('(2+3)*7 = ' + result); 
                   })
         .addErrback(
                  function (error) 
                  { 
                         alert('Mmm… something wrong happened: ' + error); 
                  });
    

    Well and most importantly:

    Also popular now: