JSDeferred 源碼分析

不經意看到了一個構思很是驚人的異步流程控制庫,發出來分享下html

http://cho45.stfuawsc.com/jsdeferred/ jquery

關於CommonJS Promises請看另外一個異步庫 http://www.cnblogs.com/aaronjs/p/3168588.htmlweb

整個代碼很是簡潔,易用,不過呢是小日本寫的東西…數組

API手冊:瀏覽器

加載jsdeferred定義延遲對象。爲方便起見,咱們用Deferred.define()方法把接口導出到全局做用於中dom

Deferred.define();

經過這樣作,你就能使用如 next(), loop(), call(), parallel() and wait() 這樣的全局函數方法,讓咱們抒寫一些異步的流程異步

next(function () {
    alert("Hello!");
    return wait(5);
}).
next(function () {
    alert("World!");
});

這個流程中,開始會彈出 「Hello」,而後過5秒接着會彈出 「world」函數

Deferred.next(function () {
    alert("Hello!");
    return Deferred.wait(5);
}).
next(function () {
    alert("World!");
});

上面是抒寫同上oop

 

我的分析:post

用Deferred.define()方法,無疑污染了全局做用域,入侵性太強了,跟mootools,prototype同樣, 不過好處嘛,很明顯,簡單易用了

還好JSDeferred也提供了無侵入的寫法

亮源碼:

Deferred.define = function (obj, list) {
    if (!list) list = Deferred.methods;
    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;
};

能夠傳入一個對象,用做上下文

var o = {}; //定義一個對象 
Deferred.define(o);//把Deferred的方法加持到它上面,讓o成爲一個Deferred子類。
o.next(function(){ 
  /* 處理 */
})

Deferred.methods = ["parallel", "wait", "next", "call", "loop", "repeat", "chain"];

 

next方法

  • next方法是能夠進行鏈式操做,鏈式的原理很簡單就是要返回當前的的this上下文才能夠
  • 因此很明顯第一個next咱們必需要建一個上下文對象提供給後面next引用,其實跟jquery鏈式一個道理

根據源碼看來Deferred.next其實就是一個靜態方法

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

顯而易見,next方法是有四種選擇優先級,爲何要這樣呢?

  • 目的是用於提供了一個JSDeferred實例與實現第一個異步操做。
  • 異步操做在JSDeferred中有許多實現方法,如setTimeout,img.onerror或 script.onreadystatechange ,
  • 它會視瀏覽器選擇最快的異步方式

我是基於webkit的遊覽器,因此咱們就直接看Deferred.next_faster_way_Image

// Modern Browsers
    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);
    };
    img.src = "data:image/png," + Math.random();
    if (fun) d.callback.ok = fun;
    return d;

流程:

  • 建立一個Deferred對象
  • 建立Image對象,實現異步
  • 監聽事件
  • if (fun) d.callback.ok = fun;  放置回調處理對象
  • 返回當前deferred對象

因而可知

next(fn).next(fn).next(fn)

其實就是

第一個 next()    Deferred.next_faster_way_Image () 返回 d

第二個 next()   d.next()

第二個next(),其實就是實例Deferred類的原型方法了

具體咱們看

next: function (fun) {
        return this._post("ok", fun)
    },
_post: function (okng, fun) {
        this._next = new Deferred();
        this._next.callback[okng] = fun;
        return this._next;
    },

看到_post方法,就有撥開雲霧見月明的感受了

其實內部會有從新生成一個 Deferred對象掛到父實例的 next上,在綁定回調..

如此依次循環處理

 

因此初始化的時候其實內部就生成了這麼一個隊列

image

_next 上都掛着下一個隊列處理

此時都是在初始化準備好的了,在執行的時候 Image 在成功回調中,咱們調用了 d.call();

執行實例的call方法

call: function (val) {
        return this._fire("ok", val)
    },
_fire: function (okng, value) {
        var next = "ok";
        try {
            value = this.callback[okng].call(this, value);
        } catch (e) {
            next = "ng";
            value = e;
            if (Deferred.onerror) Deferred.onerror(e);
        }
        if (Deferred.isDeferred(value)) {
            value._next = this._next;
        } else {
            if (this._next) this._next._fire(next, value);
        }
        return this;
    }
  • 取出當前實例的回調方法,傳入參數
  • 返回的value 是否仍是一個isDeferred對象
  • 若是沒有返回值,繼續調用內部的_next上的Deferred實例,依次循環

 

總結:

構思很特別,把每次鏈式的回調掛到內部的next子屬性中,在處理上也保持了一條線的引用關係,而不是常規的用數組的方式存起來

固然上面僅僅只是同步方法的處理分析,也不是標準的遵循CommonJS Promises規範去抒寫的

相關文章
相關標籤/搜索