前言:
前一篇文章中重點總結了一下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: 如有描述錯誤,請幫忙指正,如果你們有不明白的地方也可以發郵件給我,