天天看點

jQuery回調、遞延對象總結(下篇) —— 解密jQuery.when方法

前言:

前一篇文章中重點總結了一下then方法,它主要用來處理多個異步任務按順序執行,即前一個任務處理完了,再繼續下一個,以此類推;

而這一章節jQuery.when方法也是處理多個異步任務,它把多個異步任務(Promise對象)合并為一個Promise對象,這個合并後的Promise對象

到底是如何來更新它的狀态,即何時執行,拒絕?讓我們繼續往下看吧!

jQuery回調、遞延對象總結篇索引:

jQuery回調、遞延對象總結(上篇)—— jQuery.Callbacks

jQuery回調、遞延對象總結(中篇) —— 神奇的then方法

jQuery回調、遞延對象總結(下篇) —— 解密jQuery.when方法

設計思路(意圖)

執行jQuery.when将會傳回一個Promise對象,我們稱作由when生成的Promise對象,如果給定的所有Promise對象均已執行,就立即執行

由when方法産生的Promise對象,如果給定的Promise對象中有任意一個被拒絕,就立即拒絕由when生成的Promise對象,這樣做的意圖

好像就是為了解決這樣一種需求:在指定的多個異步事件都完成了,然後才能幹自己想幹的事情

源碼解析

jQuery.extend({
    // Deferred helper
    // 參數為Promise對象,我們稱作:給定的Promise對象
    // when函數内部的deferred對象,我們稱作:由when生成的Promise對象
    when: function( subordinate /* , ..., subordinateN */ ) {
        var i = 0,
            resolveValues = core_slice.call( arguments ),
            length = resolveValues.length,
            // the count of uncompleted subordinates
            // 用來存儲給定的未執行(解決)的Promise對象的個數
            remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

            // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
            // 我們稱deferred為when生成的Promise對象
            deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

            // Update function for both resolve and progress values
            updateFunc = function( i, contexts, values ) {
                return function( value ) {
                    contexts[ i ] = this;
                    values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
                    // 如果給定的任意一個Promise對象未執行或拒絕,則通知由when生成的Promise對象為pending狀态
                    // 注:contexts是由所有給定的Promise對象組成的數組,
                    // values是由處理所有給定的Promise對象的回調的參數組成的數組
                    if( values === progressValues ) {
                        deferred.notifyWith( contexts, values );
                    }
                    // 如果給定的Promise對象已執行(解決),且當未執行的Promise對象個數為0,
                    // 即:給定的所有Promise對象都已執行(解決),則立即執行由when生成的Promise對象
                    // 注:contexts是由所有給定的Promise對象組成的數組,
                    // values是由處理所有給定的Promise對象的回調的參數組成的數組(請看執行個體1)
                    else if ( !( --remaining ) ) {
                        deferred.resolveWith( contexts, values );
                    }
                };
            },

            progressValues, progressContexts, resolveContexts;

        // add listeners to Deferred subordinates; treat others as resolved
        if ( length > 1 ) {
            progressValues = new Array( length );
            progressContexts = new Array( length );

            // resolveValues = core_slice.call( arguments ) // 别忘了最上面的這行代碼
            resolveContexts = new Array( length );
            for ( ; i < length; i++ ) {
                // 如果給定when的參數是一個Promise對象,則通知由when生成的Promise對象,通知什麼,如何通知?
                // 如果給定的Promise對象已執行,則執行由when生成的Promise對象(要等到所有給定Promise對象全部執行)
                // 如果給定的任意一個Promise對象已拒絕,則拒絕由when生成的Promise對象
                // 如果未執行或拒絕,預設是pending狀态
                if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
                    resolveValues[ i ].promise()
                        .done( updateFunc( i, resolveContexts, resolveValues ) )
                        .fail( deferred.reject )
                        .progress( updateFunc( i, progressContexts, progressValues ) );
                }
                // 如果給定的不是一個Promise對象,那麼負責減一
                else {
                    --remaining;
                }
            }
        }

        // if we're not waiting on anything, resolve the master
        // 如果傳遞給when的參數都不是遞延對象,則執行由when生成的Promise對象
        if ( !remaining ) {
            // resolveContexts為一個空數組new Array( length ),resolveValues是由when參數組成的一個數組
            deferred.resolveWith( resolveContexts, resolveValues );
        }

        return deferred.promise();
    }
});      

執行個體:關于執行由when生成的Promise對象的參數的問題

var promiseA = $.Deferred();
var promiseB = $.Deferred();

var doneFn = function(arg){
    console.log(arg);
};

promiseA.done(doneFn);
promiseB.done(doneFn);

promiseA.resolve('A'); // 'A'
promiseB.resolve('B'); // 'B'

var whenFn = function(arg1, arg2){
    console.log(this[0] === promiseA.promise()); // true
    console.log(this[1] === promiseB.promise()); // true
    console.log('promise' + arg1 + ', promise' + arg2 + ' All done!');
};
var whenPromise = jQuery.when(promiseA, promiseB);
whenPromise.done(whenFn); // true true 'promiseA, promiseB All done!'      

jQuery回調、遞延對象總結:

遞延對象中的then方法作用于使多個異步任務按照順序執行,而jQuery.when方法作用于在多個并發的異步任務執行完畢後再幹自己感興趣的事情;

jQuery遞延對象是基于jQuery回調對象架構的,如果你想熟練掌握jQuery遞延對象,請先移步jQuery.Callbacks對象

PS: 如有描述錯誤,請幫忙指正,如果你們有不明白的地方也可以發郵件給我,

  如需轉載,請附上本文位址及出處:部落格園華子yjh,謝謝!