31 May 2009

Deferred для Javascript (Prototype)

Website development
Prototype and Twisted
Продолжая тему Deferred для JavaScript предлагаю еще одно переписывание Deferred, теперь в терминах Prototype. Подробнее о самом Deferred можно почитать в двух моих прошлых заметках: Асинхронное программирование: концепция Deferred и Deferred: все подробности. Если кратко, самое распространенное и полезное применение Deferred в JavaScript — это работа с AJAX или другими RPC-over-HTTP вызовами, когда необходимо совершить цепочку логически связанных вызовов, корректно обрабатывать возникающие ошибки и т.п. С моей точки зрения, Deferred крайне необходим в таких ситуациях.

Перейдем к примерам: обращение к некоторому JSON-RPC API на основе Prototype’овского Ajax.Request можеть быть обернуто в Deferred следующим образом:

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;
    },
});

Здесь любое обращение к Api.call будет возвращать новый Deferred, который будет содержать результат удаленного вызова либо исключение (транспортное или от сервера, к которому мы обращаемся). Пусть есть RPC-вызовы sum и mult, которые, соответственно, складывают и перемножают свои аргументы. Тогда вычисление выражения (2+3)*7 с использованием нашего класса Api будет выглядеть так:

  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); 
              });

Ну и самое главное:
Tags:deferredjavascriptprototypetwisted
Hubs: Website development
+21
2.9k 36
Comments 6