文章出處

一、前言                            

   最近在研究Promises/A+規范及實現,而Promise/A+規范的制定則很大程度地參考了由日本geek cho45發起的jsDeferred項目(《JavaScript框架設計》提供該資訊,再次感謝),追本溯源地了解jsDeferred是十分有必要的,并且當你看過官網(http://cho45.stfuawsc.com/jsdeferred/)的新手引導后就會有種不好好學學就太可惜的感覺了,而只看API和使用指南是無法滿足我對它的好奇心的,通過解讀源碼讀透它的設計思想才是根本。

  本文部分內容將和《JS魔法堂:剖析源碼理解Promises/A》中的內容作對比來講解。

  由于內容較多,特設目錄一坨

    二、jsDeferred與Promises/A的核心區別

    三、從API說起

  四、細說功能實現

  1. 基礎功能部分

    1.1. 構造函數  

    1.2. call函數

    1.3. fail函數

    1.4. next函數

    1.5. error函數

  2. 輔助功能部分

    2.1. Deferred.define函數

    2.2. Deferred.isDeferred函數

    2.3. Deferred.wait函數

    2.4. Deferred.next函數

    2.5. Deferred.call函數

    2.6. Deferred.loop函數

    2.7. Deferred.parallel函數

    2.8. Deferred.earlier函數

    2.9. Deferred.chain函數

    2.10. Deferred.connect函數

    2.11. Deferred.register函數

    2.12. Deferred.retry函數

    2.13. Deferred.repeat函數

  五、總結

    六、參考

 

二、jsDeferred與Promises/A的核心區別              

  jsDeferred的特點

  ①. 內部通過單向鏈表結果存儲 成功事件處理函數、失敗事件處理函數 和 鏈表中下一個Deferred類型對象;

  ②. Deferred實例內部沒有狀態標識(也就是說Deferred實例沒有自定義的生命周期);

  ③. 由于Deferred實例沒有狀態標識,因此不支持成功/失敗事件處理函數的晚綁定;

  ④. Deferred實例的成功/失敗事件是基于事件本身的觸發而被調用的;

  ⑤. 由于Deferred實例沒有狀態標識,因此成功/失敗事件可被多次觸發,也不存在不變值作為事件處理函數入參的說法;

  Promises/A的特點:

  ①. 內部通過單向鏈表結果存儲 成功事件處理函數、失敗事件處理函數 和 鏈表中下一個Promise類型對象;

  ②. Promise實例內部有狀態標識:pending(初始狀態)、fulfilled(成功狀態)和rejected(失敗狀態),且狀態為單方向移動“pending->fulfilled","pending->rejected";(也就是Promse實例存在自定義的生命周期,而生命周期的每個階段具備不同的事件和操作)

  ③. 由于Promise實例含狀態標識,因此支持事件處理函數的晚綁定;

  ④. Promise實例的成功/失敗事件函數是基于Promise的狀態而被調用的。

  

  核心區別

      Promises調用成功/失敗事件處理函數的兩種流程

         ①. 調用resolve/reject方法嘗試改變Promise實例的狀態,若成功改變其狀態,則調用Promise當前狀態相應的事件處理函數;(類似于觸發onchange事件)

         ②. 通過then方法進行事件綁定,若Promise實例的狀態不是pending,則調用Promise當前狀態相應的事件處理函數。

         由上述可以知道Promises的成功/失敗事件處理函數均基于Promise實例的狀態而被調用,而非成功/失敗事件。

      jsDeferred調用成功/失敗事件處理函數的流程

         ①. 調用call/fail方法觸發成功/失敗事件,則調用相應的事件處理函數。

         因此jsDeferred的是基于事件的。

 

三、從API說起                            

  下列內容均為大概介紹API接口,具體用法請參考官網。

  1. 構造函數

      Deferred ,可通過 new Deferred() 或 Deferred() 兩種方式創建Deferred實例。

  2. 實例方法

   Deferred next({Function} fn) ,綁定成功事件處理函數,返回一個新的Deferred實例。

       Deferred error({Function} fn) ,綁定失敗事件處理函數,返回一個新的Deferred實例。

       Deferred call(val*) ,觸發成功事件,返回一個新的Deferred實例。

   Deferred fail(val*) ,觸發失敗事件,返回一個新的Deferred實例。

  3. 靜態屬性

       {Function} Deferred.ok ,默認的成功事件處理函數。

   {Function} Deferred.ng ,默認的失敗事件處理函數。

       {Array} Deferred.methods ,默認的向外暴露的靜態方法。(供 Deferred.define方法 使用)

  4. 靜態方法

       {Function}Deferred Deferred.define(obj, list) ,暴露list制定的靜態方法到obj上,obj默認是全局對象。

       Deferred Deferred.call({Function} fn [, arg]*) ,創建一個Deferred實例并且觸發其成功事件。

       Deferred Deferred.next({Function} fn) ,創建一個Deferred實例并且觸發其成功事件,其實就是無法傳入參到成功事件處理函數的 Deferred.call() 。

       Deferred Deferred.wait(sec) ,創建一個Deferred實例并且等sec秒后觸發其成功事件。

   Deferred Deferred.loop(n, fun) ,循環執行fun并且上一個fun,最后一個fun的返回值將作為Deferred實例的成功事件處理函數的入參。

       Deferred Deferred.parallel(dl) ,將dl中非Deferred對象轉換為Deferred對象,然后并行觸發dl中的Deferred實例的成功事件,當

所有Deferred對象均調用了成功事件處理函數后,返回的Deferred實例則觸發成功事件,并且所有返回值將被封裝為數組作為Deferred實例的成功事件處理函數的入參。

       Deferred Deferred.earlier(dl) ,將dl中非Deferred對象轉換為Deferred對象,然后并行觸發dl中的Deferred實例的成功事件,當

其中一個Deferred對象調用了成功事件處理函數則終止其他Deferred對象的觸發成功事件,而返回的Deferred實例則觸發成功事件,并且那個被調用的成功事件處理函數的返回值為Deferred實例的成功事件處理函數的入參。

       Boolean Deferred.isDeferred(obj) ,判斷obj是否為Deferred類型。

   Deferred Deferred.chain(args) ,創建一個Deferred實例一次執行args的函數

   Deferred Deferred.connect(funo, options) ,將某個函數封裝為Deferred對象

   Deferred Deferred.register(name, fn) ,將靜態方法附加到Deferred.prototype上

   Deferred Deferred.retry(retryCount, funcDeferred, options) ,嘗試調用funcDeffered方法(返回值類型為Deferred)retryCount,直到觸發成功事件或超過嘗試次數為止。

   Deferred Deferred.repeat(n, fun) ,循環執行fun方法n次,若fun的執行事件超過20毫秒則先將UI線程的控制權交出,等一會兒再執行下一輪的循環。

   jsDeferred采用DSL風格的API設計,語義化我喜歡啊!

四、細說功能實現                          

  1. 基礎功能部分

    1.1. 構造函數

function Deferred () { return (this instanceof Deferred) ? this.init() : new Deferred() }
// 默認的成功事件處理函數
Deferred.ok = function (x) { return x };
// 默認的失敗事件處理函數
Deferred.ng = function (x) { throw  x };
Deferred.prototype = {
    // 初始化函數
    init : function () {
    this._next    = null;
    this.callback = {
        ok: Deferred.ok,
        ng: Deferred.ng
    };
    return this;
}};

    1.2. call函數

Deferred.prototype.call = function (val) { return this._fire("ok", val) };
Deferred.prototype._filre = function(okng, value){
  var next = "ok";
  try {
    // 調用當前Deferred實例的事件處理函數
    value = this.callback[okng].call(this, value);
  } catch (e) {
    next = "ng";
    value = e;
    if (Deferred.onerror) Deferred.onerror(e);
  }
  if (Deferred.isDeferred(value)) {
    // 若事件處理函數返回一個新Deferred實例,則將新Deferred實例的鏈表指針指向當前Deferred實例的鏈表指針指向,
    // 這樣新Deferred實例的事件處理函數就會先與原鏈表中其他Deferred實例的事件處理函數被調用。
    value._next = this._next;
  } else {
    if (this._next) this._next._fire(next, value);
  }
  return this;
};

    1.3. fail函數

Deferred.prototype.fail = function (err) { return this._fire("ng", err) };

    1.4. next函數

Deferred.prototype.next = function (fun) { return this._post("ok", fun) };
Deferred.prototype._post = function (okng, fun) {
    // 創建一個新的Deferred實例,插入Deferred鏈表尾,并將事件處理函數綁定到新的Deferred上  
    this._next = new Deferred();
    this._next.callback[okng] = fun;
    return this._next;
};

    《JS魔法堂:剖析源碼理解Promises/A》中的官網實現示例是將事件處理函數綁定到當前的Promise實例,而不是新創的Promise實例。而jsDeferred則是綁定到新創建的Deferred實例上。這是因為Promise實例默認的事件處理函數為undefined,而Deferred是含默認的事件處理函數的。

    1.5. error函數

Deferred.prototype.error = function (fun) { return this._post("ng", fun) };

  2. 輔助功能部分

  jsDeferred的基礎功能部分都十分好理解,我認為它的精彩之處在于類方法——輔助功能部分。

    2.1. Deferred.define函數實現

Deferred.define = function (obj, list) {
    if (!list) list = Deferred.methods;
    // 以全局對象作為默認的入潛目標
    // 由于帶代碼運行在sloppy模式,因此函數內的this指針指向全局對象。若運行在strict模式,則this指針值為undefined。
    // 即使被以strict模式運行的程序調用,本段程序依然以sloppy模式運行使用
    if (!obj) obj = (function getGlobal () { return this })();
    for (var i = 0; i < list.length; i++) {
    var n = list[i];
        obj[n] = Deferred[n];
    }
    return Deferred;
};

    當我第一次看新手引導中的示例代碼

Deferred.define();
next(function(){
  ............
}).next(function(){
  ...............
});

    這不是就和jdk1.5的靜態導入 import static一樣嗎?!兩者同樣是以入侵的方式將類方法附加到當前執行上下文中,這種導入的方式有人喜歡有人明令禁止(原上下文被破壞,維護性大大降低)。而我則有一個準則,就是導入的類方法足夠少(5個左右,反正能看一眼API就記得那種),團隊的小伙伴們均熟知這些API,并且僅以此方式導入一個類的方法到當前執行上下文中。其實能滿足這些要求的庫不多,還不如起個短小精干的類名作常規導入更實際。這里扯遠了,我再看看 Deferred.define方法 吧,其實它除了將類方法導入到當前執行上下文,還可以導入到一個指定的對象中(這個方法比較中用!)

var ctx = {};
Deferred.define(ctx);
ctx.next(function(){
   ..............
}).next(function(){
   .............
});

    2.2. Deferred.isDeferred函數實現

Deferred.isDeferred = function (obj) {
    return !!(obj && obj._id === Deferred.prototype._id);
};
// 貌似是Mozilla有個插件也叫Deferred,因此不能通過instanceof來檢測。cho45于是自定義標志位來作檢測,并在github上提交fxxking Mozilla,哈哈!
Deferred.prototype._id = 0xe38286e381ae;

    2.3. Deferred.wait函數實現

Deferred.wait = function (n) {
    var d = new Deferred(), t = new Date();
    var id = setTimeout(function () {
        // 入參為實際等待毫秒數,由于各瀏覽器的setTimeout均有一個最小精準度參數(IE9+和現代瀏覽器為4msec,IE5.5~8為15.4msec),因此實際等待的時間一定被希望的長
        d.call((new Date()).getTime() - t.getTime());
    }, n * 1000);
    d.canceller = function () { clearTimeout(id) };
    return d;
}; 

    剛看到該函數時我確實有點小雞凍,我們可以將《JS魔法堂:剖析源碼理解Promises/A》的第三節“從感性領悟”下的示例,寫得于現實生活的思路更貼近了。

// 任務定義部分
var 下班 = function(){};
var 搭車 = function(){};
var 接小孩 = function(){};
var 回家 = function(){};

// 流程部分
next(下班)
    .wait(10*60)
    .next(下班)
    .wait(10*60) 
    .next(搭車)
    .wait(10*60) 
    .next(接小孩)
    .wait(20*60)
    .next(回家);

    2.4. Deferred.next函數實現

      該函數可為是真個jsDeferred最出彩的地方了,也是后續其他方法的實現基礎,它的功能是創建一個新的Deferred對象,并且異步執行該Deferred對象的call方法來觸發成功事件。針對運行環境的不同,它提供了相應的異步調用的實現方式并作出降級處理。

Deferred.next = 
    Deferred.next_faster_way_readystatechange ||
    Deferred.next_faster_way_Image ||
    Deferred.next_tick ||
    Deferred.next_default;

      由淺入深,我們先看看使用setTimeout實現異步的 Deferred.next_default方法 (存在最小時間精度的問題)

Deferred.next_default = function (fun) {
    var d = new Deferred();
    var id = setTimeout(function () { d.call() }, 0);
    d.canceller = function () { clearTimeout(id) };
    if (fun) d.callback.ok = fun;
    return d;
};

      然后是針對nodejs的 Deferred.next_tick方法 

Deferred.next_tick = function (fun) {
    var d = new Deferred();
    // 使用process.nextTick來實現異步調用 
    process.nextTick(function() { d.call() });
    if (fun) d.callback.ok = fun;
    return d;
};

      然后就是針對現代瀏覽器的 Deferred.next_faster_way_Image方法 

Deferred.next_faster_way_Image = function (fun) {
    var d = new Deferred();
    var img = new Image();
    var handler = function () {
        d.canceller();
        d.call();
    };
    img.addEventListener("load", handler, false);
    img.addEventListener("error", handler, false);
    d.canceller = function () {
        img.removeEventListener("load", handler, false);
        img.removeEventListener("error", handler, false);
    };
    // 請求一個無效data uri scheme導致馬上觸發load或error事件
    // 注意:先綁定事件處理函數,再設置圖片的src是個良好的習慣。因為設置img.src屬性后就會馬上發起請求,假如讀的是緩存那有可能還未綁定事件處理函數,事件已經被觸發了。
    img.src = "data:image/png," + Math.random();
    if (fun) d.callback.ok = fun;
    return d;
};

      最后就是針對IE5.5~8的 Deferred.next_faster_way_readystatechange方法 

Deferred.next_faster_way_readystatechange = ((typeof window === 'object') && (location.protocol == "http:") && !window.opera && /\bMSIE\b/.test(navigator.userAgent)) && function (fun) {
    var d = new Deferred();
    var t = new Date().getTime();
    /* 原理:
            由于瀏覽器對并發請求數作出限制(IE5.5~8為2~3,IE9+和現代瀏覽器為6),
             因此當并發請求數大于上限時,會讓請求的發起操作排隊執行,導致延時更嚴重了。
       實現手段:
            以150毫秒為一個周期,每個周期以通過setTimeout發起的異步執行作為起始,
            周期內的其他異步執行操作均通過script請求實現。
            (若該方法將在短時間內被頻繁調用,可以將周期頻率再設高一些,如100毫秒)
    */
    if (t - arguments.callee._prev_timeout_called < 150) {
        var cancel = false;
        var script = document.createElement("script");
        script.type = "text/javascript";
        // 采用無效的data uri sheme馬上觸發readystate變化
        script.src  = "data:text/javascript,";
        script.onreadystatechange = function () {
            // 由于在一次請求過程中script的readystate會變化多次,因此通過cancel標識來保證僅調用一次call方法
            if (!cancel) {
                d.canceller();
                d.call();
            }
        };
        d.canceller = function () {
            if (!cancel) {
                cancel = true;
                script.onreadystatechange = null;
                document.body.removeChild(script);
            }
        };
        // 不同于img元素,script元素需要添加到dom樹中才會發起請求
        document.body.appendChild(script);
    } else {
        arguments.callee._prev_timeout_called = t;
        var id = setTimeout(function () { d.call() }, 0);
        d.canceller = function () { clearTimeout(id) };
    }
    if (fun) d.callback.ok = fun;
    return d;
};

    2.5. Deferred.call函數實現

Deferred.call = function (fun) {
    var args = Array.prototype.slice.call(arguments, 1);
        // 核心在Deferred.next
    return Deferred.next(function () {
        return fun.apply(this, args);
    });
};

    2.6. Deferred.loop函數實現

Deferred.loop = function (n, fun) {
    // 入參n類似于Python中range的效果
    // 組裝循環的配置信息
    var o = {
        begin : n.begin || 0,
        end   : (typeof n.end == "number") ? n.end : n - 1,
        step  : n.step  || 1,
        last  : false,
        prev  : null
    };
    var ret, step = o.step;
    return Deferred.next(function () {
        function _loop (i) {
            if (i <= o.end) {
                if ((i + step) > o.end) {
                    o.last = true;
                    o.step = o.end - i + 1;
                }
                o.prev = ret;
                ret = fun.call(this, i, o);
                if (Deferred.isDeferred(ret)) {
                    return ret.next(function (r) {
                        ret = r;
                        return Deferred.call(_loop, i + step);
                    });
                } else {
                    return Deferred.call(_loop, i + step);
                }
            } else {
                return ret;
            }
        }
        return (o.begin <= o.end) ? Deferred.call(_loop, o.begin) : null;
    });
};

    上述代碼的理解難點在于Deferred實例A的事件處理函數若返回一個新的Deferred實例B,而實例A的Deferred鏈表中原本指向Deferred實例C,那么當調用實例A的call方法時是實例C的事件處理函數先被調用,還是實例B的事件處理函數先被調用呢?這時只需細讀 Deferred.prototype.call方法 的實現就迎刃而解了,答案是先調用實例B的事件處理函數哦!

    2.7. Deferred.parallel函數實現

Deferred.parallel = function (dl) {
    // 對入參作處理
    var isArray = false;
    if (arguments.length > 1) {
        dl = Array.prototype.slice.call(arguments);
        isArray = true;
    } else if (Array.isArray && Array.isArray(dl) || typeof dl.length == "number") {
        isArray = true;
    }

    var ret = new Deferred(), values = {}, num = 0;
    for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) {
        // 若d為函數類型,則封裝為Deferred實例
        // 若d既不是函數類型,也不是Deferred實例則報錯哦!
        if (typeof d == "function") 
            dl[i] = d = Deferred.next(d);
        d.next(function (v) {
            values[i] = v;
            if (--num <= 0) {
                // 湊夠數就觸發事件處理函數
                if (isArray) {
                    values.length = dl.length;
                    values = Array.prototype.slice.call(values, 0);
                }
                ret.call(values);
            }
        }).error(function (e) {
            ret.fail(e);
        });
        num++;
    })(dl[i], i);

    // 當dl為空時觸發Deferred實例的成功事件
    if (!num) Deferred.next(function () { ret.call() });
    ret.canceller = function () {
        for (var i in dl) if (dl.hasOwnProperty(i)) {
            dl[i].cancel();
        }
    };
    return ret;
};

    通過源碼我們可以知道parallel的入參必須為函數或Deferred實例,否則會報錯哦!

    2.8. Deferred.earlier函數實現

Deferred.earlier = function (dl) {
    // 對入參作處理
    var isArray = false;
    if (arguments.length > 1) {
        dl = Array.prototype.slice.call(arguments);
        isArray = true;
    } else if (Array.isArray && Array.isArray(dl) || typeof dl.length == "number") {
        isArray = true;
    }
    var ret = new Deferred(), values = {}, num = 0;
    for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) {
        // d只能是Deferred實例,否則拋異常
        d.next(function (v) {
            values[i] = v;
            // 一個Deferred實例觸發成功事件則終止其他Deferred實例觸發成功事件了
            if (isArray) {
                values.length = dl.length;
                values = Array.prototype.slice.call(values, 0);
            }
            ret.call(values);
            ret.canceller();
        }).error(function (e) {
            ret.fail(e);
        });
        num++;
    })(dl[i], i);

    // 當dl為空時觸發Deferred實例的成功事件
    if (!num) Deferred.next(function () { ret.call() });
    ret.canceller = function () {
        for (var i in dl) if (dl.hasOwnProperty(i)) {
            dl[i].cancel();
        }
    };
    return ret;
};

    通過源碼我們可以知道earlier的入參必須為Deferred實例,否則會報錯哦!

    2.9. Deferred.chain函數實現

Deferred.chain = function () {
    var chain = Deferred.next();
    // 生成Deferred實例鏈表,鏈表長度等于arguemtns.length
    for (var i = 0, len = arguments.length; i < len; i++) (function (obj) {
        switch (typeof obj) {
            case "function":
                var name = null;
                // 通過函數名決定是訂閱成功還是失敗事件
                try {
                    name = obj.toString().match(/^\s*function\s+([^\s()]+)/)[1];
                } catch (e) { }
                if (name != "error") {
                    chain = chain.next(obj);
                } else {
                    chain = chain.error(obj);
                }
                break;
            case "object":
                // 這里的object包含形如{0:function(){}, 1: Deferred實例}、Deferred實例
                chain = chain.next(function() { return Deferred.parallel(obj) });
                break;
            default:
                throw "unknown type in process chains";
        }
    })(arguments[i]);
    return chain;
};

    2.10. Deferred.connect函數實現

Deferred.connect = function (funo, options) {
    var target, // 目標函數所屬的對象
        func, // 目標函數
        obj; // 配置項
    if (typeof arguments[1] == "string") {
        target = arguments[0];
        func   = target[arguments[1]];
        obj    = arguments[2] || {};
    } else {
        func   = arguments[0];
        obj    = arguments[1] || {};
        target = obj.target;
    }

    // 預設定的入參
    var partialArgs       = obj.args ? Array.prototype.slice.call(obj.args, 0) : [];
    // 指出成功事件的回調處理函數位于原函數的入參索引
    var callbackArgIndex  = isFinite(obj.ok) ? obj.ok : obj.args ? obj.args.length : undefined;
    // 指出失敗事件的回調處理函數位于原函數的入參索引
    var errorbackArgIndex = obj.ng;

    return function () {
        // 改造成功事件處理函數,將預設入參和實際入參作為成功事件處理函數的入參
        var d = new Deferred().next(function (args) {
            var next = this._next.callback.ok;
            this._next.callback.ok = function () {
                return next.apply(this, args.args);
            };
        });

        // 合并預設入參和實際入參
        var args = partialArgs.concat(Array.prototype.slice.call(arguments, 0));
        // 打造func的成功事件處理函數,內部將觸發d的成功事件
        if (!(isFinite(callbackArgIndex) && callbackArgIndex !== null)) {
            callbackArgIndex = args.length;
        }
        var callback = function () { d.call(new Deferred.Arguments(arguments)) };
        args.splice(callbackArgIndex, 0, callback);

        // 打造func的失敗事件處理函數,內部將觸發d的失敗事件
        if (isFinite(errorbackArgIndex) && errorbackArgIndex !== null) {
            var errorback = function () { d.fail(arguments) };
            args.splice(errorbackArgIndex, 0, errorback);
        }
        // 相當于setTimeout(function(){ func.apply(target, args) })
        Deferred.next(function () { func.apply(target, args) });
        return d;
    };
};

     如何簡化將setTimeout、setInterval、XmlHttpRequest等異步API封裝為Deferred對象(或Promise)對象的步驟是一件值思考的事情,而jsDeferred的connect類方法提供了一個很好的范本。

    2.11. Deferred.register函數實現

Deferred.register = function (name, fun) {
    this.prototype[name] = function () {
        var a = arguments;
        return this.next(function () {
            return fun.apply(this, a);
        });
    };
};

Deferred.register("loop", Deferred.loop);
Deferred.register("wait", Deferred.wait);

    2.12. Deferred.retry函數實現

Deferred.retry = function (retryCount, funcDeferred, options) {
    if (!options) options = {};

    var wait = options.wait || 0; // 嘗試的間隔時間,存在最小時間精度所導致的延時問題
    var d = new Deferred();
    var retry = function () {
        // 有funcDeferred內部觸發事件
        var m = funcDeferred(retryCount);
        m.next(function (mes) {
                d.call(mes);
            }).
            error(function (e) {
                if (--retryCount <= 0) {
                    d.fail(['retry failed', e]);
                } else {
                    setTimeout(retry, wait * 1000);
                }
            });
    };
    // 異步執行retry方法
    setTimeout(retry, 0);
    return d;
};

    2.13. Deferred.repeat函數實現

Deferred.repeat = function (n, fun) {
    var i = 0, end = {}, ret = null;
    return Deferred.next(function () {
        var t = (new Date()).getTime();
        // 當fun的執行耗時小于20毫秒,則馬上繼續執行下一次的fun;
        // 若fun的執行耗時大于20毫秒,則將UI線程控制權交出,并將異步執行下一次的fun。
        // 從而降低因循環執行耗時操作使頁面卡住的風險。
        do {
            if (i >= n) return null;
            ret = fun(i++);
        } while ((new Date()).getTime() - t < 20);
        return Deferred.call(arguments.callee);
    });
};

 

五、總結                                

  通過剖析jsDeferred源碼我們更深刻地理解Promises/A和Promises/A+規范,也了解到setTimeout的延時問題和通過img、script等事件縮短延時的解決辦法(當然這里并沒有詳細記錄解決辦法的細節),最重要的是吸取大牛們的經驗和了解API設計的藝術。但這里我提出一點對jsDeferred設計上的吐槽,就是Deferred實例的私有成員還是可以通過實例直接引用,而不像Promises/A官網實現示例那樣通過閉包隱藏起來。

  尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4141918.html  ^_^肥子John

 

六、參考                                

《JavaScript框架設計》

jsDeferred官網

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()