簡要:zepto的deferred.js 並不遵照promise/A+ 規範,而在jquery v3.0.0中的defer在必定程度上實現了promise/A+ ,所以本文主要研究jquery v3.0.0中的defer。html
首先 在上源碼前,本人以爲有必要認識一下promise/A+ 規範:https://segmentfault.com/a/1190000002452115python
接下來上源碼:jquery
define( [ "./core", "./var/slice", "./callbacks" ], function( jQuery, slice ) { "use strict"; function Identity( v ) { return v; } function Thrower( ex ) { throw ex; } function adoptValue( value, resolve, reject ) { var method; try { // Check for promise aspect first to privilege synchronous behavior if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { method.call( value ).done( resolve ).fail( reject ); // Other thenables } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { method.call( value, resolve, reject ); // Other non-thenables } else { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context // 假設value是常量,resolve會馬上調用,而且傳入參數爲value resolve.call( undefined, value ); } // For Promises/A+, convert exceptions into rejections // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in // Deferred#then to conditionally suppress rejection. } catch ( value ) { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context // 這裏執行master.reject(value) reject.call( undefined, value ); } } jQuery.extend( { Deferred: function( func ) { //元組:描述狀態、狀態切換方法名、對應狀態執行方法名、回調列表的關係 //tuple引自C++/python,和list的區別是,它不可改變 ,用來存儲常量集 var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", //Promise初始狀態 //promise對象,promise和deferred的區別是: /*promise只包含執行階段的方法always(),then(),done(),fail(),progress()及輔助方法state()、promise()等。 deferred則在繼承promise的基礎上,增長切換狀態的方法,resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()*/ //因此稱promise是deferred的只讀副本 promise = { /** * 返回狀態 * @returns {string} */ state: function() { return state; }, /** * 成功/失敗狀態的 回調調用 * @returns {*} */ always: function() { deferred.done( arguments ).fail( arguments ); return this; }, // TODO 待解釋 "catch": function( fn ) { return promise.then( null, fn ); }, /** * * @returns promise對象 */ // Keep pipe for back-compat pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; //注意,這不管如何都會返回一個新的Deferred只讀副本, //因此正常爲一個deferred添加成功,失敗,千萬不要用pipe,用done,fail return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { // Map tuples (progress, done, fail) to arguments (done, fail, progress) // 根據tuple,從fns裏取出對應的fn var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; // deferred.progress(function() { bind to newDefer or newDefer.notify }) // deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.fail(function() { bind to newDefer or newDefer.reject }) //註冊fn的包裝函數 deferred[ tuple[ 1 ] ]( function() { //直接執行新添加的回調 fnDone fnFailed fnProgress var returned = fn && fn.apply( this, arguments ); //返回結果是promise對象 if ( returned && jQuery.isFunction( returned.promise ) ) { //轉向fnDone fnFailed fnProgress返回的promise對象 //注意,這裏是兩個promise對象的數據交流 //新deferrred對象切換爲對應的成功/失敗/通知狀態,傳遞的參數爲 returned.promise() 給予的參數值 returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { //新deferrred對象切換爲對應的成功/失敗/通知狀態 newDefer[ tuple[ 0 ] + "With" ]( this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; //depth == 0; //special == newDefer.notifyWith,用來判斷是否爲pending狀態觸發 //deferred == jQuery.Deferred() 返回的新的defer對象即then()返回的對象即newDefer //handler == [onFulfilled,onRejected,onProgress]||Identity,Identity爲簡單返回形參的fn, //在resolve返回的fn中handle以deferred爲上下文執行 function resolve( depth, deferred, handler, special ) { //這個fn會在then的調用者對應的回調列表中,是handler的包裝函數,在此稱之爲wrappeHandler; return function() { //這個this是不定的,好比 fn.call(obj,[args]) var that = this, args = arguments, //TODO 這是什麼功能? mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts // 通常是0和0,1和1,有種狀況是0和1 // defer.then(sfn,rfn,nfn);nfn先執行並返回promise,該promise會then(fn1), // 注:此fn1是由resolve(depth = 0)生成,所以fn1內部執行時depth == 0 // 接下來執行sfn,此時返回promise1,maxDepth++, // 若是接下來promise被resolve了,便會執行fn1,便會出現上述狀況 if ( depth < maxDepth ) { return; } //傳入的handler以當前上下文和參數執行 returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 // newPromise1 = defer.then(function(){return newPromise}); // newPromise在resolve時執行回調函數fn1,而fn1執行newPromise1的回調函數, // 若newPromise1 === newPromise,則會出現死循環 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once // 若是有returned.then,則returned爲promise then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( jQuery.isFunction( then ) ) { // Special processors (notify) just wait for resolution // special可判斷此wrappeHandler在pending列表中仍是在resolver或reject中 if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { //若是這個包裝函數wrappeHandler是在resolve列表或reject列表中 //newDefer = defer.then(function(){return promise}); //defer在resolve時候執行function(){return promise}的包裝函數,在此包裝函數中則會執行到此 //即要執行到此,則必須知足 1:此包裝函數對應的defer resolve and reject,2:hander 返回promise // ...and disregard older resolution values // notify裏面返回的promise在resolve後的參數可能會傳遞給第2個then去執行 // 若是在這以前resolve已經將參數傳遞給了第2個then,這裏要防止老數據 maxDepth++; /* * returned.then(resolve( maxDepth, deferred, Identity, special )) * deferred:下文中的newDefer,做用是Identity()執行後,newDefer.resolveWidth * */ then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) /* * newDefer = defer.then(fn1).then(fn2); * defer註冊fn1,將封裝了fn1的包裝函數bfn1加入到defer的回調列表中,newDefer註冊fn2,同理有bfn2 * 這裏是 handler 返回的是普通對象,則newDefer當即resolve的即當即執行fn2 * 若是走defer走resolve流程時,此時fn1 === handler的則newDefer.resolve(fn1的that,fn1的args); * 若是fn1返回的returned 走resolve流程,此時handler === identity,則newDefer.resolve(undefined,identity的return); * */ if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve // 不管then裏面的函數返回的promise是notify,resolve,reject,最終都會執行resolveWith ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions // 這裏是對異常的處理?? process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { /*jQuery.Deferred.exceptionHook = function( error, stack ) { // Support: IE 8 - 9 only // Console exists when dev tools are open, which can happen at any time if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); } };*/ if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions // 正常的註冊結構出現異常會走下面流程 // TODO 暫不清楚 depth + 1 < maxDepth 的狀況 if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } //正常狀況下第一個then的resolve和reject出現異常,會致使第二個then裏面的reject執行 deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // 最開始的觸發 // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async // TODO 不太明白 if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } /* * defer.then(fn) --> * 建立newDefer --> * defer關聯的回調列表(下文中的tuples[ * ][ 3 ])增長一個fn的包裝函數(由resolve生成), * 這個包裝函數執行fn,並對其返回值和newDefer作出相應處理 --> * 返回newDefer * */ return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, jQuery.isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); //返回defer的只讀版本promise }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object /** * 返回obj的promise對象 * @param obj * @returns {*} */ promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, //內部封裝deferred對象 deferred = {}; // Add list-specific methods //給deferred添加切換狀態方法 jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add //擴展promise的done、fail、progress爲Callback的add方法,使其成爲回調列表 //簡單寫法: promise['done'] = jQuery.Callbacks( "once memory" ).add // promise['fail'] = jQuery.Callbacks( "once memory" ).add promise['progress'] = jQuery.Callbacks( "memory" ).add promise[ tuple[ 1 ] ] = list.add; // Handle state //切換的狀態是resolve成功/reject失敗 //添加首組方法作預處理,修改state的值,使成功或失敗互斥,鎖定progress回調列表, if ( stateString ) { /* if (stateString) { list.add(function(){ state = stateString //i^1 ^異或運算符 0^1=1 1^1=0,成功或失敗回調互斥,調用一方,禁用另外一方 }, tuples[i^1][2].disable, tuples[2][2].lock) } */ list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } //添加切換狀態方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith() deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise //deferred繼承promise的執行方法 promise.promise( deferred ); // Call given func if any //傳遞了參數func,執行 if ( func ) { func.call( deferred, deferred ); } // All done! //返回deferred對象 return deferred; }, // Deferred helper /** * * 主要用於多異步隊列處理。 多異步隊列都成功,執行成功方法,一個失敗,執行失敗方法 也能夠傳非異步隊列對象 * @param sub * @returns {*} */ when: function( singleValue ) { var // count of uncompleted subordinates remaining = arguments.length, // count of unprocessed arguments i = remaining, // subordinate fulfillment data resolveContexts = Array( i ), resolveValues = slice.call( arguments ), //隊列數組 ,未傳參數是[],slice能將對象轉化爲數組 // the master Deferred // 這是主分支 .when().then(),master決定.then()的執行 master = jQuery.Deferred(), // subordinate callback factory // .when()參數中每個value被resolve後調用下面的返回函數 // 1:將每個調用者和調用參數存在數組裏,2: 最後以數組做爲參數,由master.resolve updateFunc = function( i ) { return function( value ) { resolveContexts[ i ] = this; // updateFunc()(v1,v2,v3),resolveValues[ i ] = [v1,v2,v3],若只有一個參數, // 則resolveValues[ i ] = value resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { //若是這是最後一個resolveValues被解決 master.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve if ( remaining <= 1 ) { // 將第二個和第三個參數註冊到第一個參數裏面去 // 若是singleValue是常量,則馬上執行master.resolve,下面的判斷不會執行 adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject ); // Use .then() to unwrap secondary thenables (cf. gh-3000) // if ( master.state() === "pending" || jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { return master.then(); } } // Multiple arguments are aggregated like Promise.all array elements // 循環爲resolveValues[i] 註冊updateFunc()方法 --> 判斷計數到最後一個則執行 master.resolve // resolveValues[i].reject-->list.add(master.reject); while ( i-- ) { // 當resolveValues[ i ]爲常量時,會馬上執行updateFunc( i ), // 若是全部的都爲常量,則 執行master.resolve(resolveValues) adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); } return master.promise(); } } ); return jQuery; } );
以上內容主要是針對then,pipe,when 作了更新與修改,segmentfault
pipe:jquery.Deferred(fn)裏面會先新建一個newDefer,而後傳入newDefer做爲參數並執行fn,fn會將pipe裏的參數依次加入到回調列表中,而且判斷參數返回值是否爲promise,若果是,則將newDefer的狀態轉化器注入到promise的回調列表中,不然,直接讓newDefer發生相應的狀態轉化。數組
測試代碼以下:promise
var defer = $.Deferred(); defer.pipe(function(data){ console.log(data); var defer1 = $.Deferred(); setTimeout(function () { defer1.resolve("second pipe"); },1000); return defer1 }).pipe(function (data) { console.log(data); }); defer.resolve("first pipe"); 結果: first pipe second pipe
then:主要流程以下:app
function then(fnDone,fnFail,fnPro){ var maxDepth = 0; //聲明調用深度,這裏的鏈式調用實爲遞歸調用 function resolve().... //resolve封裝了then的參數 var defer = jQuery.Deferred(function (newDefer) { Tuples.add(resolve(fnDone),resolve(fnFail),resolve(fnPro)); }) }
這裏主要流程在resolve函數裏面,resolve返回一個封裝函數fn,這個fn的主要功能是管理和執行resovle中的handle參數,mightThrow 方法主要實現基本的promise功能異步
若是fnDone和fnFail出現了異常,則捕獲異常,並newDefer.reject(); async
以下例子:ide
var defer = $.Deferred(); defer.then(function () { throw new Error("resolve error"); }).then(function () { console.log("second then resolve"); },function (data) { console.log("second then reject"); console.log(data); }); defer.resolve(); 結果: second then reject Error: resolve error at file:///defer.html:10:19 at jQuery.extend.Deferred.promise.then.mightThrow (file:///jquery-3.0.0.js:3507:29) at jQuery.extend.Deferred.promise.then.process (file:///jquery-3.0.0.js:3576:12)
mightThrow方法 首先執行 returned = handler.apply(that,args); 若是returned的類型是promise,則將newDefer的狀態轉換交給returned的回調列表進行管理,若爲常量,則直接調用newDefer.resolve(); 從pending狀態到resolve狀態其中的流程大可能是不可控的,爲避免pending的數據影響resolve ,因而用depth來區分。
defer.when: 傳一個或一組參數,能夠是常量也能夠是promise,這裏有兩個地方我以爲是很是好的,第一個是把when中每一個參數完成後的計數操做提取出來造成一個函數
updateFunc(i),第二個是adoptValue ,將master的狀態變化注入到每一個參數的回調列表中由其統一管理。
以下例子:
$.when("no-promise").then(function (data) { console.log(data + "Execution without delay!"); }); 結果: no-promiseExecution without delay! var defer1 = $.Deferred(); var defer2 = $.Deferred(); var defer3 = $.Deferred(); $.when(defer1,defer2,defer3).then(function (d1,d2,d3) { console.log(d1); console.log(d2); console.log(d3); }); setTimeout(function () { defer1.resolve("defer1"); },1000); setTimeout(function () { defer2.resolve("defer2"); },2000); setTimeout(function () { defer3.resolve("defer3"); },3000); 結果: defer1 defer2 defer3
注:本人小菜一枚,如有不通之處,敬請指教