Deferred
最近在看deferred,有一些疑問,于是研究了一下,現在理解程度也就是70%-80%
這個方法基于callbacks,所以說,如果要看原理,最好先看一下callbacks的源碼。下面我貼上源碼,進行分析一下,會加上自己的理解,我主要困惑在when和then方法上,會重點說一下,這里說一下基本的源碼分析網上有很多,大家可以搜一下,這里鏈接一個when和then方法的例子地址:
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
http://www.css88.com/jqapi-1.9/deferred.then/
ok,進入源碼jQuery.extend({
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;
//直接返回一個deferred的方法,即直接返回一個deferred對象,
//這里的newDefer就是我們的return jQuery.Deferred(。。)的返回的新的deferred對象,源碼Deferred方法的參數如果是函數的話
//會立即執行,源碼如下:if ( func ) {func.call( deferred, deferred );},此時會把deferred作為參數傳遞進去
return jQuery.Deferred(function( newDefer ) { //循環遍歷tuples,分別對不同狀態注冊函數 jQuery.each( tuples, function( i, tuple ) //取出“動作”:resolve(解決)、reject(拒絕)、notify(通知) var action = tuple[ 0 ],
//取出對應回調函數,這里的i用的比較巧妙,如果fns[i]不存在返回false,否則返回函數 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer
//這里dererred的各個狀態的add方法添加了newDefer的fire方法,以后deferred調用fire的話,這個新的newDefer也同樣fire
deferred[ tuple[1] ](function() {
//這里注意一下,這里相當于if(fn){returned = fn.apply(this,arguments)};
//這時候的returned相當于函數fn執行完畢之后的結果,后面舉例子,就會看到效果了,這個this和arguments就是在fire的時候傳遞進來的,源碼如下(for each中體現的):
//deferred[ tuple[0] ] = function() {deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );return this;}; var returned = fn && fn.apply( this, arguments );
//如果返回的結果是一個deferred對象,則把newDefer跟這個deferred對象關聯,保證一個fire引起另外一個fire if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else {
//直接調用各個狀態的fireWith方法,如果fn存在就直接把returned放入 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擴展obj,即把promise的屬性都復制到到obj中,如果obj不存在則直接返回promise
//promise跟deferred區別在于deferred有很多可以改變對象的方法(rejectWith、resolveWith等),promise沒有promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compatpromise.pipe = promise.then; // Add list-specific methods
//這里主要就是把tuples的各個屬性進行一一解析賦值到defferred中,
jQuery.each( tuples, function( i, tuple ) {
//list就是各個狀態的callbacks的list,stateString是狀態 var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add
//把各個狀態的done、fail、progress方法直接掛到callbacks的add上面
promise[ tuple[1] ] = list.add; // Handle state
//如果狀態存在,也就是tuples的前兩組
if ( stateString ) {
//done和fail狀態,分別添加三個函數(改變自己的state,把另外一個狀態設置為不可用,把第三個狀態鎖死),這樣為了保證只有一個狀態執行例如:done的時候fail和process就不能執行了 list.add(function() { // state = [ resolved | rejected ]state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
//這個學一下,i^1,這個是異或運算符,效率高,特點:只有一個是1的時候才等于1
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ]
//定義fireWith方法,所謂的修改方法
deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; };
//把fireWith跟callbacks的fireWith進行綁定 deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise
//擴展deferred,把promise的屬性擴展到defrred中
promise.promise( deferred ); // Call given func if any
//剛才上面提到的,Deferred()的參數如果是函數,則立即調用,參數就是將要返回的deferred對象
if ( func ) { func.call( deferred, deferred ); } // All done!return deferred; }, // Deferred helper
//這個方法是jq的擴展,他的作用就是把放入其中的N個異步方法做一個計數,知道最后一個方法完成之后,才執行when后面的方法
when: function( subordinate /* , ..., subordinateN */ ) {
//定義變量,resolveValues把參數轉化成數組 var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates
//remaining存放參數長度,也就是通過它來通知when是否所有的異步方法都執行完了
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
//如果remaining等于1,則肯定是deferred,如果參數大于1,則新定義一個deferred
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values
//更新remaining長度,以及判斷是否是最后一個異步,如果是最后一個,則調用deferred的fire方法,從而觸發when()后面的方法調用
updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) {
//計數為0的時候,調用deferred的fire方法 deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolvedif ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) {
//判斷傳進來的參數是否是deferred,不是則直接計數減一,是的話,調用deferred的promise()方法返回promise對象,然后添加updateFunc函數,
//當參數中的函數fire的時候,就調用updateFunc函數,計數減一,直到為0
//整體思路,就是定義一個全局變量保存總共的參數個數,然后寫一個公共函數,分配到各個參數函數中,每次函數執行完畢,就執行公共函數去操作公共變量減一,
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the masterif ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } });
?when函數例子:
var dtd = $.Deferred(); // 新建一個deferred對象
var wait = function(dtd){
var tasks = function(){
alert("執行完畢!");
dtd.resolve(); // 改變deferred對象的執行狀態
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出錯啦!"); });
?
then函數例子:
var filterResolve = function() {
var defer = $.Deferred(),
filtered = defer.then(function( value ) {
return value * 2;
});
defer.resolve( 5 );
filtered.done(function( value ) {
alert(value);//10
});
};
filterResolve();
說說這個then的執行邏輯,在源代碼中也提到過
?1)定義一個deferred對象defer
?2)defer的then方法執行過程,可以這么理解
a)var filtered = $.Deferred(),fn =?function(value){return value*2;}
b)defer.add(filtered.resolve(fn()));
當調用defer的add的時候,相當于filtered的fire方法,而fire的參數是fn()函數的返回值,
當filtered再次done(add方法)的時候,直接執行上次的fire方法,而參數也是上一個的參數,參見callbacks的 once memory特性
?
轉載于:https://www.cnblogs.com/aishangyizhihu/p/4232142.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: win7 64 下安装ubuntu14.
- 下一篇: [数据结构]二叉树