什麼叫作遞延對象,生成一個遞延對象只需調用jQuery.Deferred函數,deferred這個單詞譯爲延期,推遲,即延遲的意思,那麼在jQuery中html
又是如何表達延遲的呢,從遞延對象中的then方法或許能找到這種延遲的行爲,本文重點解讀遞延對象中的then方法ajax
jQuery回調、遞延對象總結篇索引:promise
jQuery回調、遞延對象總結(上篇)—— jQuery.Callbacks服務器
jQuery回調、遞延對象總結(中篇) —— 神奇的then方法app
jQuery回調、遞延對象總結(下篇) —— 解密jQuery.when方法異步
在遞延對象構造中,分別有三組回調對象,每一組回調對象都有與之對應的行爲(action,add listener),和狀態(final state),ide
這些行爲都概括爲遞延對象中的(觸發回調,添加函數到回調列表中等)方法函數
Deferred構造源碼除了then函數源碼外,其餘都很是簡單,這裏不作過多解讀,後面將重點討論then方法this
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; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], 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[ 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: 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 // 能夠看看上篇中lock方法的各類場景調用 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { // 若是方法不被借用,那麼回調中的this對象爲promise,沒有觸發回調的方法 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 // 做用於then方法 if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; } });
在實際項目應用中,一個頁面或許有多個ajax請求,你可能會這樣作:url
$.ajax({ url1, ... });
$.ajax({ url2, ... });
$.ajax({ url3, ... });
...
這樣作的缺點:
一、多個ajax同時發送請求,可能會形成服務器壓力,對於富應用頁面來講,若是請求過多,那是必然的;
二、對於頁面底部,或者說首屏不展現給用戶瀏覽的部分須要發送的ajax請求,沒有必要讓它一開始加載頁面後就發送請求,這樣會形成頁面響應緩慢
jQuery遞延對象中的then方法好像天生就是爲了解決以上問題而設計的,它能夠按照順序依次處理多個異步請求,即第一個請求處理完後,
再處理第二個請求,以此類推,這樣既能夠減輕服務器壓力,又能夠先發送首屏(從上到下)頁面部分的請求,使頁面響應更快
來看看一段很是優雅的實例代碼
var promiseA = $.get(urlA); promiseA.always(doneFnA, failFnA, progressFnA); var promiseB = promiseA.then(function(){ return $.get(urlB); }); promiseB.always(doneFnB, failFnB, progressFnB);
或者你也能夠這樣寫,但並不建議:
var promiseB = $.get(urlA).then(function(){ var state = this.state(); // 針對第一個ajax請求的處理 switch (state) { case 'resolved' : doneFnA(); break; case 'rejected' : failFnA(); break; case 'pending' : progressA(); break; default: break; } return $.get(urlB); }); promiseB.always(doneFnB, failFnB, progressB);
上面代碼是如何運行的呢:
首先發送第一個ajax請求,當promiseA對象執行過resolve(或reject、notify)後,即:第一個請求成功或失敗後,將依次執行回調doneFnA
(或failFnA、progressFnA),then中的匿名函數(注意代碼的順序,以前代碼順序有誤,把promiseA.always放在了then方法執行以後,現已改過來了),
匿名函數中發送第二個ajax請求,當請求成功或失敗後,將執行對應的回調函數(doneFnB或failFnB、progressFnB)
衍生後的代碼
var promiseA = $.get(urlA); // 這裏添加promiseA的回調 var promiseB = promiseA.then(function(){ return $.get(urlB); }); // 這裏添加promiseB的回調 var promiseC = promiseB.then(function(){ return $.get(urlC); }); // 這裏添加promiseC的回調 var promiseD = promiseC.then(function(){ return $.get(urlD); }); // 這裏添加promiseD的回調
再來看看then函數中的構造源碼,經過上面的實例分析,相信眼前的你會恍然大悟的
promise = { then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; // 返回後的promise對象與newDefer對應 return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], 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 ); // 若是回調返回的是一個遞延對象,newDefer將根據這個返回的遞延對象的狀態來觸發行爲 if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } // 若是回調返回的不是一個遞延對象,newDefer將根據第一個(deferred)遞延對象的狀態來觸發行爲 else { newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); } }
PS: 若有描述錯誤,請幫忙指正,若是大家有不明白的地方也能夠發郵件給我,