jQuery-v2.0.3源碼淺析04-Deferred

接下來我們來看下jQuery的延遲對象Deferred。
我們前面講過Callbacks函數(shù),其實Deferred就是在此基礎(chǔ)進行了擴展,Deferred是對異步函數(shù)的統(tǒng)一管理。

源碼

/**源碼2999行**/
jQuery.extend({
    Deferred: function( func ) {
        var tuples = [
                // 類似Callbacks的fire, 類似Callbacks的add, Callbacks, 執(zhí)行完畢的狀態(tài)
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ],
            state = "pending",//沒有執(zhí)行之前狀態(tài)默認為state
            promise = {
                state: function() {
                    return state;
                },
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                then: function( /* fnDone, fnFail, fnProgress */ ) {
                    var fns = arguments;
                    return jQuery.Deferred(function( newDefer ) {
                        jQuery.each( tuples, function( i, tuple ) {
                            var action = tuple[ 0 ],
                                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                            // deferred[ done | fail | progress ] for forwarding actions to newDefer
                            deferred[ tuple[1] ](function() {
                                var returned = fn && fn.apply( this, arguments );
                                if ( returned && jQuery.isFunction( returned.promise ) ) {
                                    returned.promise()
                                        .done( newDefer.resolve )
                                        .fail( newDefer.reject )
                                        .progress( newDefer.notify );
                                } else {
                                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                                }
                            });
                        });
                        fns = null;
                    }).promise();
                },
                // Get a promise for this deferred
                // If obj is provided, the promise aspect is added to the object
                promise: function( obj ) {
                    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 ],
                stateString = tuple[ 3 ];

            // promise[ done | fail | progress ] = list.add
            promise[ tuple[1] ] = list.add;

            // Handle state
            if ( stateString ) {
                list.add(function() {
                    // state = [ resolved | rejected ]
                    state = stateString;

                // [ reject_list | resolve_list ].disable; progress_list.lock
                }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
            }

            // deferred[ resolve | reject | notify ]
            deferred[ tuple[0] ] = function() {
                deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
                return this;
            };
            deferred[ tuple[0] + "With" ] = list.fireWith;
        });

        // Make the deferred a promise
        promise.promise( deferred );

        // Call given func if any
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    }
});

根據(jù)代碼我們能夠解構(gòu)出該函數(shù)主要的兩個對象promise 和 deferred

promise解構(gòu)

promise = {
  state,
  always,
  pipe = then,
  promise,
  done,
  fail,
  progress
}

deferred解構(gòu)

deferred = {
  state,
  always,
  pipe = then,
  promise,
  done,
  fail,
  progress,
  resolve,
  reject,
  notify,
  resolveWith,
  rejectWith,
  notifyWith
}

相信看到這里很多理解了Callbacks的同學(xué)已經(jīng)發(fā)現(xiàn)了,其實deferred 和 promise的區(qū)別其實就是,deferred對象提供了Callbacks的"fire"方法,而promise只有Callbacks的"add"。不過肯定很多同學(xué)肯定有跟我一樣的疑惑,為什么要這樣設(shè)計呢?其實也是比較好理解的,就拿ajax來說吧,打比方說我們ajax獲取數(shù)據(jù)是否成功的狀態(tài)能夠被我們自己改變嗎,答案是不能。所以deferred是提供給"內(nèi)部方法"使用的,如果給"外部"使用我們就返回promise對象。例如:

function getDef(){
    var def = $.Deferred();
    //模擬延遲操作
    setTimeout(function(){
        def.resolve();
    }, 1000);
    return def.promise();
}
var def = getDef();
def.done(function(){
    console.log(1);
});

這個時候getDef()所返回的promise已經(jīng)不包含'fire'方法,所以狀態(tài)不會被隨便串改。

假設(shè)我們上面getDef函數(shù)直接返回def,是不是會存在下面這種情況

function getDef(){
    var def = $.Deferred();
    //模擬延遲操作
    setTimeout(function(){
        def.resolve();
    }, 1000);
    return def;
}
var def = getDef();
def.done(function(){
    console.log(1);
});
def.resolve();
//發(fā)現(xiàn)程序一執(zhí)行控制臺就輸出1了。

接下來我們來具體看一下源碼是怎么實現(xiàn)的吧。

/**源碼3046行**/
var tuples = [
  [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
  [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
  [ "notify", "progress", jQuery.Callbacks("memory") ]
]
/**源碼3051行**/
jQuery.each( tuples, function( i, tuple ) {
    var list = tuple[ 2 ],
        stateString = tuple[ 3 ];

    // promise[ done | fail | progress ] = list.add
    /**源碼3056行**/
    promise[ tuple[1] ] = list.add;

    // Handle state
    /**源碼3059行**/
    if ( stateString ) {
        list.add(function() {
            // state = [ resolved | rejected ]
            state = stateString;

        // [ reject_list | resolve_list ].disable; progress_list.lock
        }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
    }

    // deferred[ resolve | reject | notify ]
    /**源碼3069行**/
    deferred[ tuple[0] ] = function() {
        deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
        return this;
    };
    deferred[ tuple[0] + "With" ] = list.fireWith;
});

首先來看下tuples變量吧
1、resolve、reject、notify 等于 Callbacks 方法中提供的fire方法。源碼3069行進行的賦值。
2、done、fail、progress 等于Callbacks 方法中提供的add方法。源碼3056行進行的賦值。
3、源碼3059行,會發(fā)現(xiàn)源碼對 state = [ resolved | rejected ] 的list,add了方法,其實這段代碼的意思是指 當執(zhí)行玩resolve和reject,對state的狀態(tài)進行修改,如果執(zhí)行完resolve(成功)就不能再執(zhí)行reject(失?。┝恕2M行中的list進行了鎖定操作。

接下來我們來看一下稍微復(fù)雜點的then方法,首先貼上源碼

/**源碼3017行**/
then: function( /* fnDone, fnFail, fnProgress */ ) {
    var fns = arguments;
    return jQuery.Deferred(function( newDefer ) {
        jQuery.each( tuples, function( i, tuple ) {
            var action = tuple[ 0 ],
                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
            // deferred[ done | fail | progress ] for forwarding actions to newDefer
            /**源碼3024行**/
            deferred[ tuple[1] ](function() {
                var returned = fn && fn.apply( this, arguments );
                if ( returned && jQuery.isFunction( returned.promise ) ) {
                    /**源碼3071行**/
                    returned.promise()
                        .done( newDefer.resolve )
                        .fail( newDefer.reject )
                        .progress( newDefer.notify );
                } else {
                                        /**源碼3076行**/
                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                }
            });
        });
        fns = null;
    }).promise();
}

了解源碼之前我們先來看看then有哪些寫法,首先是第一種比較簡單的寫法,可以分別傳3個回調(diào)函數(shù)分別對應(yīng)3個狀態(tài)的回調(diào)函數(shù)

function getDef(){
    var def = $.Deferred();
    //模擬延遲操作
    setTimeout(function(){
        def.resolve();
        //def.reject();
        //def.notify();
    }, 1000);
    return def;
}
var def = getDef();
def.then(function(){
    console.log('成功');
}, function(){
    console.log('失敗');
}, function(){
    console.log('進行中');
});

該功能的實現(xiàn)代碼是在源碼的3024行實現(xiàn)的,將傳入的三個函數(shù)分別add [done | fail | progress] 到對應(yīng)的list中,最后直接通過var returned = fn && fn.apply( this, arguments );這句來執(zhí)行回調(diào)函數(shù)。

then的第二種使用方法

function getDef(){
    var def = $.Deferred();
    //模擬延遲操作
    setTimeout(function(){
        def.resolve();
        // def.resolve();
        // def.notify();
    }, 1000);
    return def;
}
var def = getDef();
def.then(function(){
    console.log('成功');
    return '123';
}, function(){
    console.log('失敗');
}, function(){
    console.log('進行中');
}).then(function(a){
    console.log(a);
}, function(){
    console.log('失敗1');
}, function(){
    console.log('進行中1');
});

then每次執(zhí)行完畢之后都會返回一個promise(包含then方法),所以一直通過then進行鏈接。然后通過源碼3076行進行參數(shù)傳遞。如果return的是一個Deferred對象,則可以通過3071行實現(xiàn)鏈式寫法。例如:

function getDef(){
    var def = $.Deferred();
    //模擬延遲操作
    setTimeout(function(){
        def.resolve();
        // def.resolve();
        // def.notify();
    }, 1000);
    return def.promise();
}
var def = getDef();
def.then(function(){
    console.log('成功');
    var def1 = getDef();
    return def1;
}).done(function(){
    console.log('成功1');
});

程序執(zhí)行1秒之后打印成功,然后再過一秒打印出成功1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 突然就醒了, 在這個數(shù)九隆冬的凌晨2:30。 竟然記得醒前的夢, 不美不驚, 只是有一點點尷尬。 為曾經(jīng)的年少不知...
    三得喵閱讀 178評論 0 0

友情鏈接更多精彩內(nèi)容