jquery Deferred 延遲對象的讀書筆記-源碼分析

Deferred:延遲對象,對異步的統一管理

基本使用

Deferred延遲對象是基於Callbacks對象的,因此要先熟悉Callbacksjquery

Deferred提供方法鏈的形式進行函數的封裝數組

DeferredDeferred做爲jquery的工具方法,封裝了3中狀態promise

1.已完成閉包

var dfd = $.Deferred();
setTimeout(function () {
    console.log("set time out");
    dfd.resolve();//控制檯輸出success
},1000);

dfd.done(function () {
    console.log("success");
}).fail(function () {
    console.log("fail");
}).progress(function () {
    console.log("progress");
});

2.未完成app

var dfd = $.Deferred();
setTimeout(function () {
    console.log("set time out");
    dfd.reject();//控制檯輸出fail
},1000);

dfd.done(function () {
    console.log("success");
}).fail(function () {
    console.log("fail");
}).progress(function () {
    console.log("progress");
});

3.進行中異步

var dfd = $.Deferred();
setInterval(function () {
    console.log("set time out");
    dfd.notify();//控制檯1s間隔輸出progress
},1000);

dfd.done(function () {
    console.log("success");
}).fail(function () {
    console.log("fail");
}).progress(function () {
    console.log("progress");
});

 

Deferred(func)參數

參數說明:ide

1.func將做爲異步函數傳入Deferred對象,並立刻進行調用函數

var dfd = $.Deferred(
    function () {
        alert(111);//控制檯彈出111
    }
);

參數處理:工具

if ( func ) {
    //Deferred中判斷有參數傳入後進行立刻調用,並將自身對象傳入改函數
    func.call( deferred, deferred );
}

 

Deferred的私有成員

1.tuples[][]:保存Deferred須要使用的數組源碼分析

2.state:臨時Deferred的狀態變量,缺省爲pending

3.promise{}:保存Deferred的私有方法

4.deferred{}:保存Deferred的公有方法

 

關於promise與deferred的具體區別:

1.deferred將做爲Deferred建立時的返回值,而promise將只在Deferred內部使用

2.promise具備成員:

promise = {
    state:function,
    alaways:function,
    then:function,
    promise:function,
    pipe:function,
    done:function,
    fail:function,
    progress:function,
}

3.deferred具備成員:

deferred = {
    state:function,
    alaways:function,
    then:function,
    promise:function,
    pipe:function,
    done:function,
    fail:function,
    progress:function,
    
    resolve:function,
    reject:function,
    notify:function,
}

4.deferred的具備的成員比promise的多一些,利用這一特性,能夠作一些有用的事,

如:禁止修改狀態值

function test() {
    var dfd = $.Deferred();
    setTimeout(function () {
        dfd.resolve();
        alert(dfd.state());
    }, 1000);
    return dfd;
}
var newDfd = test();
newDfd.done(function () {
    alert('成功');
}).fail(function () {
    alert('失敗');
});
newDfd.reject();//alert的是失敗和rejected,狀態值被修改了

改爲這樣:

function test() {
    var dfd = $.Deferred();
    alert(dfd.state());
    setTimeout(function () {
        dfd.resolve();
        alert(dfd.state());
    }, 1000);
    return dfd.promise();//promise()轉換成了promise
}
var newDfd = test();
newDfd.done(function () {
    alert('成功');
}).fail(function () {
    alert('失敗');
});
newDfd.reject();//alert的是成功和resolved,狀態值沒被修改

 

promise的成員

1.state:返回Deferred的狀態

state: function() {
    return state;
},

2.always:我的理解爲老是執行的意思,調用了done和fail方法,無論調用的是 deferred.resolve() 仍是 deferred.reject(),都會調用想對應的方法

always: function() {
    deferred.done( arguments ).fail( arguments );
    return this;
},

3.then:傳遞3個參數,分別是done、fail和progress方法,分別調用執行封裝一個快捷方式

then: function( /* fnDone, fnFail, fnProgress */ ) {
    var fns = arguments;// 參數爲傳入的 done 、 fail 、progress 函數

    //閉包
    //1。先建立一個新的Deferred,同時會執行func.call( deferred, deferred );這裏newDefer = deferred
    return jQuery.Deferred(function( newDefer ) {

                //tuples遍歷
                jQuery.each( tuples, function( i, tuple ) {

                    var action = tuple[ 0 ],//表示三種狀態 resolve|reject|notify 其中之一

                        // 分別對應 fnDone, fnFail, fnProgress(首先用 isFunction 判斷傳入的參數是不是方法,注意 && 在這裏的用法)
                        fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];//對應的回調函數

                    // deferred[ done | fail | progress ] for forwarding actions to newDefer
                    deferred[ tuple[1] ](function() {//done fail progress,本質是callbacks的add,添加一個回調函數

                        var returned = fn && fn.apply( this, arguments );//fn不爲false,執行參數的函數

                        //這裏是對pipe的函數的做用
                        // 若是回調返回的是一個 Deferred 實例
                        if ( returned && jQuery.isFunction( returned.promise ) ) {
                            // 則繼續派發事件
                            returned.promise()
                                .done( newDefer.resolve )
                                .fail( newDefer.reject )
                                .progress( newDefer.notify );
                        } else {
                            // 若是回調返回的不是一個 Deferred 實例,則被當作 args 由 XXXWith 派發出去,本質是fire
                            newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );

                        }

                    });
                });

        fns = null;// 銷燬變量,防止內存泄漏(退出前手工設置null避免閉包形成的內存佔用)

    }).promise();//這邊返回了一個新的Deferred的promise();

then這邊比較複雜,分兩種狀況

1).Deferred直接使用then,如:

var dfd = $.Deferred();
setTimeout(function(){
    //dfd.resolve();
    dfd.reject('hello world');
},1000);

dfd.then(function () {
    alert(1);
}, function () {
    alert(arguments[0]);
}, function () {
    alert(3);
});

這種比較好處理,源碼中間接對then的參數進行了遍歷,分別進行了callbacks的add操做,回調函數中的var returned = fn && fn.apply( this, arguments );這裏對函數進行了調用

2).promise.pipe = promise.then中使用then,這邊比較繞

具體看例子:

var dfd = $.Deferred();
setTimeout(function(){
    dfd.resolve('hi');
},1000);
var newDfd = dfd.pipe(function(){
    return arguments[0] + ' word';
});
newDfd.done(function(){
    alert( arguments[0] );
});

結果將會是彈出hi word,這樣能夠實現相似aop的思想

dfd.pipe的調用其實是調用了then,fns中對應的是return arguments[0] + 'word'函數,進行tuples的遍歷, deferred[ tuple[1] ](function() {}中構造了一個新的函數進行了add,因此本質上進行了3種狀態的add,即resolve|reject|notify都會進行想對一個你的函數調用

4.promise:有參數時是進行合併,沒參數直接返回promise

promise: function( obj ) {//jQuery.extend( obj, promise ) 對應deferred對象,比promise多出3個函數
    return obj != null ? jQuery.extend( obj, promise ) : promise;
}

 

deferred的成員

將promise的對象成員賦給deferred,調用extend執行

promise.promise( deferred ); // 將promise的對象成員賦給deferred,調用extend執行
promise: function( obj ) {//jQuery.extend( obj, promise ) 對應deferred對象,比promise多出3個函數
    return obj != null ? jQuery.extend( obj, promise ) : promise;
}

關於多出的3個函數:在建立deferred時同時遍歷了tuples,並將resolve | reject | notify添加到deferred中,能夠看到並無對promise進行相對應的添加

// deferred[ resolve | reject | notify ],本質是屬於fireWith方法
// deferred[ resolve | reject | notify ]
// tuple[0] == resolve | reject | notify
// 能夠看到 resolve | reject | notify 其實就是 Callbacks 裏邊的 fire 方法
deferred[ tuple[0] ] = function() {
    deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
    return this;
};

 

具體源碼分析

var tuples = [//映射數組
        // action, add listener, listener list, final state
        [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
        [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
        [ "notify", "progress", jQuery.Callbacks("memory") ]
        //done fail  progress對應Callbacks的add方法 , resolve|reject|notify對應Callbacks的fire方法
        //狀態:resolved 完成 rejected 未完成 notify進行中
        //resolve/reject只會觸發一次 notify進行屢次觸發
    ],
    state = "pending", //狀態變量,初始狀態pending 的意思爲待定

    // 具備 state、always、then、primise 方法
    promise = {

        // 返回一個 Deferred 對象的當前狀態
        state: function() {
            return state;
        },

        // 它的做用是,無論調用的是 deferred.resolve() 仍是 deferred.reject() ,最後老是執行
        always: function() {
            deferred.done( arguments ).fail( arguments );
            return this;
        },

        // 把 done()、fail() 和 progress() 合在一塊兒寫
        // deferred.done(fnDone), fail(fnFail) , progress(fnProgress) 的快捷方式
        then: function( /* fnDone, fnFail, fnProgress */ ) {
            var fns = arguments;// 參數爲傳入的 done 、 fail 、progress 函數

            //閉包
            //1。先建立一個新的Deferred,同時會執行func.call( deferred, deferred );這裏newDefer = deferred
            return jQuery.Deferred(function( newDefer ) {

                        //tuples遍歷
                        jQuery.each( tuples, function( i, tuple ) {

                            var action = tuple[ 0 ],//表示三種狀態 resolve|reject|notify 其中之一

                                // 分別對應 fnDone, fnFail, fnProgress(首先用 isFunction 判斷傳入的參數是不是方法,注意 && 在這裏的用法)
                                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];//對應的回調函數

                            // deferred[ done | fail | progress ] for forwarding actions to newDefer
                            deferred[ tuple[1] ](function() {//done fail progress,本質是callbacks的add,添加一個回調函數

                                var returned = fn && fn.apply( this, arguments );//fn不爲false,執行參數的函數

                                //這裏是對pipe的函數的做用
                                // 若是回調返回的是一個 Deferred 實例
                                if ( returned && jQuery.isFunction( returned.promise ) ) {
                                    // 則繼續派發事件
                                    returned.promise()
                                        .done( newDefer.resolve )
                                        .fail( newDefer.reject )
                                        .progress( newDefer.notify );
                                } else {
                                    // 若是回調返回的不是一個 Deferred 實例,則被當作 args 由 XXXWith 派發出去,本質是fire
                                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );

                                }

                            });
                        });

                fns = null;// 銷燬變量,防止內存泄漏(退出前手工設置null避免閉包形成的內存佔用)

            }).promise();//這邊返回了一個新的Deferred的promise();
        },
        // Get a promise for this deferred
        // If obj is provided, the promise aspect is added to the object
        promise: function( obj ) {//jQuery.extend( obj, promise ) 對應deferred對象,比promise多出3個函數
            return obj != null ? jQuery.extend( obj, promise ) : promise;
        }
    },
    deferred = {};

// Keep pipe for back-compat
promise.pipe = promise.then;

// Add list-specific methods
//遍歷映射數組
jQuery.each( tuples, function( i, tuple ) {

    var list = tuple[ 2 ], //jQuery.Callbacks("once memory")
        stateString = tuple[ 3 ];// stateString 爲最後的狀態s

    // promise[ done | fail | progress ] = list.add 本質是屬於回調對象Callable的方法
    promise[ tuple[1] ] = list.add; //Callbacks的add方法

    // Handle state
    if ( stateString ) {

        //修改狀態,並使其它狀態禁止調用
        list.add(function() {
            // state = [ resolved | rejected ]
            state = stateString;

        },
            // [ reject_list | resolve_list ].disable; progress_list.lock
            // 這裏用到了 disable ,便是禁用回調列表中的回調
            // 禁用對立的那條隊列
            // 異或 0^1 = 1   1^1 = 0
            // 便是成功的時候,把失敗那條隊列禁用
            // 便是成功的時候,把成功那條隊列禁用
            tuples[ i ^ 1 ][ 2 ].disable,
            // 鎖住當前隊列狀態
            tuples[ 2 ][ 2 ].lock
        );
    }

    // deferred[ resolve | reject | notify ],本質是屬於fireWith方法
    // deferred[ resolve | reject | notify ]
    // tuple[0] == resolve | reject | notify
    // 能夠看到 resolve | reject | notify 其實就是 Callbacks 裏邊的 fire 方法
    deferred[ tuple[0] ] = function() {
        deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
        return this;
    };

    // deferred[resolveWith | rejectWith | notifyWith] 調用的是 Callbacks 裏的 fireWith 方法
    deferred[ tuple[0] + "With" ] = list.fireWith;
});

// Make the deferred a promise
promise.promise( deferred ); // 將promise的對象成員賦給deferred,調用extend執行

// Call given func if any
if ( func ) {
    //把當前任務的上下文跟參數設置成當前生成的deferred實例
    func.call( deferred, deferred );
}

// All done!
// 返回實例,顯而易見 Deferred 是個工廠類,返回的是內部構建的 deferred 對象
return deferred;
相關文章
相關標籤/搜索