首先,我們先把Deferred的jQuery源碼貼出來:
Deferred: function( func ) { 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") ] ], state = "pending", 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 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[ tuple[ 0 ] + "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; }
再來一步步解析:
首先我們可以看到:
deferred = {};及
return deferred;得知在$.Callbacks()會想辨法建立好Deferred物件們並傳回。
再來我們可以看到其先準備了幾個變數:
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") ] ], state = "pending"可以看到一開始的 state被設為pending,二維陣列的tuples的前兩個一維陣列的"resolve"及"reject"剛好對應了Deferred的resolve()及reject()的函式名稱,而"done"、"fail"也對應到了Deferred的done()及fail()的函式名稱,很顯然tuples[0]跟異步函式完成有關、tuples[1]跟沒成功完成有關。
因為tuples[0]跟tuples[1]在後續的處理一樣,可以以此類推,所以我們就先只討論tuples[0]的處理,可以看到:
- tuples[0][0] = "resolve" :對應 Deferred 的 resolve() 的函式名稱;。
- tuples[0][1] = "done" :對應 Deferred 的 done() 的函式名稱;。
- tuples[0][2] = jQuery.Callbacks("once memory") :為一個專門處理異步函式成功執行相關的Callbacks 列隊。
- tuples[0][3] = "resolved":對應Deferred被 resolve() 後的狀態。
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; });
在上面的程式碼中,對tuples進行了遍歷,i為index、tuple為tuples[i],首先先用list來得到執行成功相關的Callbacks列隊,再用stateString取得tuple[3],接著對promise物件添加了一個done屬性,指向了Callbacks的add(),promise在Deferred()裡有宣告,是一個無法改變state的物件:
promise[ tuple[1] ] = list.add;接著如果有stateString的話(即tuples[0]及tuples[1]),對list放進三個函式:
- 改變state狀態,例如異步執行成功相關就是要改成"resolved"
- 如果是在處理tuples[0]的話,將tuples[1][2]指向的Callbacks列隊給disable掉;
如果是在處理tuples[1]的話,將tuples[0][2]指向的Callbacks列隊給disable掉;
其中 ^ 是 XOR 運算子:tuples[ i ^ 1 ][ 2 ].disable
- 將tuples[2][2]指向的Callabcks列隊給lock住:
tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock
function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }
deferred[ tuple[0] + "With" ] = list.fireWith;它為deferred添加了一個屬性,resolveWith,指向了list的fireWith方法。
再來回去看上面那行程式碼:
deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; };它為deferred添加了一個屬性,resolve,指向的function內容其實就是去執行deferred["solvedWith"],也就是list的fireWith。
然後,利用 promise 內設定的同名函式 (函式名也叫 promise),將 promise 所擁有的所有屬性及函式都 extend 給deferred,所以 deferred 擁有所有的函式及屬性(把括了resolve().done()、then()等等,這裡要注意的是,原本 deferred 有的 promise 這個非函式屬性,在這裡會被名為 promise 的同名函式蓋掉,也就是 deferred 只能用 promise() 來存取原本的 promise 物件了),而promise只有部份(done()、then()等等)方法
promise.promise( deferred );最後如果$.Deferred()有參入函式參數(func)的話,執行它,並且給func一個deferred參數供其利用:
// Call given func if any if ( func ) { func.call( deferred, deferred ); }
====================================================================
接著,我們再來看一下then()這個方法的實作內容:
then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var 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[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }首先,先用fns得到送入的函式參數,並且返回了一個新的 Deferred (也就是 newDefer ),並且執行送入 jQuery.Deferred() 裡作為輸入參數的函式,函式的內容為:
遍歷 tuples 二維陣列,以下只討論 i = 0 時的情況,其他以此類推:
先用 fn 得到 then() 的函式輸入,並且執行 deferred[tuple[1]](),也就是呼叫 newDefer 的 done(),其作用是把一個 function 放進 resolve 所管的列隊裡,也就是 tuples[0][2] ,而這個 function 裡面要做的事就是 newDefer 被呼叫 resolve() 後要做的事情,那要做什麼事情呢?
首先先執行送進 then() 中的函式,也就是 fn,並把呼叫 then 的 Deferred (也就是 deferred,不是 newDefer) 執行 resolve(arguments) 時送入的 arguments 送給 fn,並用 returned 得到 fn 回傳的 Deferred (所以在 fn 中可以宣告一個新的 Deferred 並傳回)。
var returned = fn && fn.apply( this, arguments );在以下的程式碼中,呼叫returned的done(), fail()等函式,並將newDefer的resolve及reject函式給其當做參數:
if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );所以在呼叫then()的Deferred做完異步函式時,就會執行給then()當參數的函式,而當參數的異步函式執行完後(別忘了在函式內宣告一個新的Deferred,並在執行完後將其resolved)就會執行呼叫then()時回傳的Deferred的resolve()。
如此一來,就可以用像
$.Deferred(function(defer1){ //do something defer1.resolve('passing argument'); }).then(function(argument){ var defer2 = $.Deferred(); //do something defer2.resolve((argument)); return defer2; }).then(function((argument)){ var defer3 = $.Deferred(); //do something defer3.resolve((argument)); return defer3; }).done(function(argument){ //do something });
這樣的程式碼串接要順序執行的異步函式,而argument可以當參數丟進resolve()中一個一個傳進then()或done()的輸入函式當輸入參數使用。
總結:
其實Deferred的done()、fail就對應到了Callbacks的add()。
而Deferred的resolve()、reject就葑應到了Callbacks的fireWith()。
其中Deferred用了2個Callbacks來管理異步函式執行成功及失敗的行為。
沒有留言 :
張貼留言