欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery源碼分析-05異步隊(duì)列 Deferred 使用介紹

 更新時(shí)間:2011年11月14日 23:51:41   作者:  
異步隊(duì)列是一個(gè)鏈?zhǔn)綄ο螅鰪?qiáng)對回調(diào)函數(shù)的管理和調(diào)用,用于處理異步任務(wù)
5. 異步隊(duì)列 Deferred
5.1 概述
異步隊(duì)列是一個(gè)鏈?zhǔn)綄ο螅鰪?qiáng)對回調(diào)函數(shù)的管理和調(diào)用,用于處理異步任務(wù)。
異步隊(duì)列有三種狀態(tài):初始化(unresolved),成功(resolved),失?。╮ejected)。
執(zhí)行哪些回調(diào)函數(shù)依賴于狀態(tài)。
狀態(tài)變?yōu)槌晒Γ╮esolved)或失敗(rejected)后,將保持不變。
回調(diào)函數(shù)的綁定可以是同步,也可以是異步的,即可以在任何時(shí)候綁定。
(本節(jié)中的 綁定 注冊 增加 具有相同的含義)
5.2 關(guān)鍵方法
先看看jQuery. Deferred()中的關(guān)鍵方法
分類
方法
說明
增加
deferred.done()
增加成功回調(diào)函數(shù)
狀態(tài)為成功(resolved)時(shí)立即調(diào)用
deferred.fail()
增加失敗回調(diào)函數(shù)
狀態(tài)為失?。╮ejected)時(shí)立即調(diào)用
deferred.then()
增加成功回調(diào)函數(shù)和失敗回調(diào)函數(shù)到各自的隊(duì)列中
便捷方法,兩個(gè)參數(shù)可以是數(shù)組或null
狀態(tài)為成功(resolved)時(shí)立即調(diào)用成功回調(diào)函數(shù)
狀態(tài)為失?。╮ejected)時(shí)立即調(diào)用失敗回調(diào)函數(shù)
deferred.always()
增加回調(diào)函數(shù),同時(shí)增加到成功隊(duì)列和失敗隊(duì)列
狀態(tài)已確定(無論成功或失?。r(shí)立即調(diào)用回調(diào)函數(shù)
執(zhí)行
deferred.resolve()
調(diào)用成功回調(diào)函數(shù)隊(duì)列
通過調(diào)用deferred.resolveWith()實(shí)現(xiàn)
deferred.resolveWith()
使用指定的上下文和參數(shù)執(zhí)行成功回調(diào)函數(shù)
deferred.reject()
調(diào)用失敗回調(diào)函數(shù)隊(duì)列
通過調(diào)用deferred.rejectWith()實(shí)現(xiàn)
deferred.rejectWith()
使用指定的上下文和參數(shù)執(zhí)行失敗回調(diào)函數(shù)隊(duì)列
其他
deferred.isRejected()
判斷狀態(tài)是否為成功(resolved)
deferred.isResolved()
判斷狀態(tài)是否為失?。╮ejected)
deferred.pipe()
每次調(diào)用回調(diào)函數(shù)之前先調(diào)用傳入的成功過濾函數(shù)或失敗過濾函數(shù),并將過濾函數(shù)的返回值作為回調(diào)函數(shù)的參數(shù)
最終返回一個(gè)只讀視圖(調(diào)用promise實(shí)現(xiàn))
deferred.promise()
返回deferred的只讀視圖
接下來將會(huì)jQuery._Deferred和jQuery.Deferred的源碼詳細(xì)剖析。


5.3 jQuery._Deferred
復(fù)制代碼 代碼如下:

局部變量
// 參考資料:
// 官網(wǎng)文檔 http://api.jquery.com/category/deferred-object/
// Deferred機(jī)制 http://www.cnblogs.com/fjzhou/archive/2011/05/30/jquery-source-3.html
// 在jQuery 1.5中使用deferred對象 http://developer.51cto.com/art/201103/248638.htm
// 拿著放大鏡看Promise http://www.cnblogs.com/sanshi/archive/2011/03/11/1981789.html
// Promises/A http://wiki.commonjs.org/wiki/Promises/A
var // Promise methods
// 注意,沒有以下方法:resolveWith resolve rejectWith reject pipe when cancel
// 即不允許調(diào)用resolve reject cancel等
promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
// Static reference to slice
// 靜態(tài)引用slice方法,借雞生蛋
sliceDeferred = [].slice;
_Deferred:
_Deferred: function() {
var // callbacks list
// 回調(diào)函數(shù)數(shù)組(這里不翻譯為隊(duì)列,避免概念上的混淆)
callbacks = [],
// stored [ context , args ]
// 存儲(chǔ)上下文、參數(shù),同時(shí)還可以標(biāo)識(shí)是否執(zhí)行完成(fired非空即表示已完成)
// 這里的“完成”指回調(diào)函數(shù)數(shù)組中“已有”的函數(shù)都已執(zhí)行完成;
// 但是可以再次調(diào)用done添加回調(diào)函數(shù),添加時(shí)fired會(huì)被重置為0
fired,
// to avoid firing when already doing so
// 如果已經(jīng)觸發(fā)正在執(zhí)行,避免再次觸發(fā)
firing,
// flag to know if the deferred has been cancelled
// 標(biāo)識(shí)異步隊(duì)列是否已被取消,取消后將忽略對done resolve resolveWith的調(diào)用
cancelled,
// 異步隊(duì)列定義(這才是正主,上邊的局部變量通過閉包引用)
// the deferred itself
deferred = {
// done( f1, f2, ...)
// 增加成功回調(diào)函數(shù),狀態(tài)為成功(resolved)時(shí)立即調(diào)用
done: function() {
// 如果已取消,則忽略本次調(diào)用
if ( !cancelled ) {
// 將后邊代碼用到的局部變量定義在代碼塊開始處的好處:
// 1.聲明變量,增加代碼可讀性;
// 2.共享變量,提高性能
// 注:多年寫Java的經(jīng)驗(yàn),養(yǎng)成了全局變量在開頭、臨時(shí)變量隨用隨定義的習(xí)慣,看來JavaScript有些不同
var args = arguments, // 回調(diào)函數(shù)數(shù)組
i, // 遍歷變量
length, // 回調(diào)函數(shù)數(shù)組長度
elem, // 單個(gè)回調(diào)函數(shù)
type, // elem類型
_fired; // 用于臨時(shí)備份fired(fired中存儲(chǔ)了上下文和參數(shù))
// 如果已執(zhí)行完成(即fired中保留了上下文和參數(shù))
// 則備份上下文和參數(shù)到_fired,同時(shí)將fired置為0
if ( fired ) {
_fired = fired;
fired = 0;
}
// 添加arguments中的函數(shù)到回調(diào)函數(shù)數(shù)組
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = jQuery.type( elem );
// 如果是數(shù)組,則遞歸調(diào)用
if ( type === "array" ) {
// 強(qiáng)制指定上下文為deferred,個(gè)人認(rèn)為這里沒必要指定上下文,因?yàn)槟J(rèn)的上下文即為deferred
deferred.done.apply( deferred, elem );
} else if ( type === "function" ) {
callbacks.push( elem );
}
}
// 如果已執(zhí)行(_fired表示Deferred的狀態(tài)是確定的),則立即執(zhí)行新添加的函數(shù)
// 使用之前指定的上下文context和參數(shù)args
if ( _fired ) {
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
}
}
return this;
},
// resolve with given context and args
// 執(zhí)行,使用指定的上下文和參數(shù)
resolveWith: function( context, args ) {
// 滿足以下全部條件,才會(huì)執(zhí)行:沒有取消 沒有正在執(zhí)行 沒有執(zhí)行完成
// 如果已取消 或 已執(zhí)行完成 或 正在執(zhí)行,則忽略本次調(diào)用
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
// 確保args可用,一個(gè)避免null、undefined造成ReferenceError的常見技巧
args = args || [];
// 執(zhí)行過程中將firing改為1
firing = 1;
try {
// 遍歷動(dòng)態(tài)數(shù)組的技巧
while( callbacks[ 0 ] ) {
// 注意這里使用指定的context,而不是this
callbacks.shift().apply( context, args );
}
}
// JavaScript支持try/catch/finally
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
// 把狀態(tài)設(shè)置為Resolved
// 設(shè)置的理解不準(zhǔn)確,因?yàn)槭欠馬esolved,是調(diào)用isResolved判斷firing、fired的狀態(tài)得到的。
// 可以理解為執(zhí)行
resolve: function() {
deferred.resolveWith( this, arguments );
return this;
},
// Has this deferred been resolved?
// 是否已執(zhí)行(或解決)?
// 在執(zhí)行或已執(zhí)行完畢,都認(rèn)為已執(zhí)行/解決
// “已”可能不準(zhǔn)確,因?yàn)閳?zhí)行過程中也認(rèn)為是已執(zhí)行
isResolved: function() {
// 正在運(yùn)行中
// 或
// 已運(yùn)行完(即fired不為空/0)
return !!( firing || fired );
},
// Cancel
// 取消異步隊(duì)列
// 設(shè)置標(biāo)記位,清空函數(shù)隊(duì)列
cancel: function() {
cancelled = 1;
callbacks = [];
return this;
}
};
return deferred;
}

5.4 jQuery.Deferred
復(fù)制代碼 代碼如下:

// Full fledged deferred (two callbacks list)
// 創(chuàng)建一個(gè)完整的異步隊(duì)列(包含兩個(gè)回調(diào)函數(shù)數(shù)組)
// 異步隊(duì)列有三種狀態(tài):初始化(unresolved),成功(resolved),失?。╮ejected)。
// 執(zhí)行哪些回調(diào)函數(shù)依賴于狀態(tài)。
// 狀態(tài)變?yōu)槌晒Γ╮esolved)或失?。╮ejected)后,將保持不變。
Deferred: function( func ) {
// _Deferred本無成功狀態(tài)或失敗狀態(tài),有四種狀態(tài):初始化、執(zhí)行中、執(zhí)行完畢、已取消
// 為了代碼復(fù)用, 內(nèi)部先實(shí)現(xiàn)了一個(gè)_Deferred
// failDeferred通過閉包引用
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// Add errorDeferred methods, then and promise
jQuery.extend( deferred, {
// 增加成功回調(diào)函數(shù)和失敗回調(diào)函數(shù)到各自的隊(duì)列中
// 便捷方法,兩個(gè)參數(shù)可以是數(shù)組或null
// 狀態(tài)為成功(resolved)時(shí)立即調(diào)用成功回調(diào)函數(shù)
// 狀態(tài)為失敗(rejected)時(shí)立即調(diào)用失敗回調(diào)函數(shù)
then: function( doneCallbacks, failCallbacks ) {
// 上下文在這里有切換:雖然done返回的是deferred,但是fail指向failDeferred.done,執(zhí)行fail是上下文變?yōu)閒ailDeferred
// 簡單點(diǎn)說就是:
// 調(diào)用done時(shí)向deferred添加回調(diào)函數(shù)doneCallbacks
// 調(diào)用fail時(shí)向failDeferred添加回調(diào)函數(shù)failCallbacks
// 因此這行表達(dá)式執(zhí)行完后,返回的是failDeferred
deferred.done( doneCallbacks ).fail( failCallbacks );
// 強(qiáng)制返回deferred
return this;
},
// 注冊一個(gè)callback函數(shù),無論是resolved或者rejected都會(huì)被 調(diào)用。
// 其實(shí),是把傳入的函數(shù)(數(shù)組),同時(shí)添加到deferred和failDeferred
// 并沒有像我想象的那樣,存到單獨(dú)的函數(shù)數(shù)組中
always: function() {
// done的上下文設(shè)置為deferred,fail的上下文設(shè)置為this
// done和fail的上下文不一致嗎?一致!在這里this等于deferred
// 但是這里如此設(shè)置上下文應(yīng)該該如何解釋呢?與then的實(shí)現(xiàn)有什么不一樣呢?
// fail指向fail指向failDeferred.done,默認(rèn)上下文是failDeferred,failDeferred的回調(diào)函數(shù)數(shù)組callbacks是通過閉包引用的,
// 這里雖然將failDeferred.done方法的上下文設(shè)置為deferred,但是不影響failDeferred.done的執(zhí)行,
// 在failDeferred.done的最后將this替換為deferred,實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,
// 即調(diào)用過程中沒有丟失上下文this,可以繼續(xù)鏈?zhǔn)秸{(diào)用其他的方法而不會(huì)導(dǎo)致this混亂
// 從語法上,always要達(dá)到的效果與then要達(dá)到的效果一致
// 因此,這行代碼可以改寫為兩行(類似then的實(shí)現(xiàn)方式),效果是等價(jià)的:
// deferred.done( arguments ).fail( arguments );
// returnr this;
return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
},
// 增加失敗回調(diào)函數(shù)
// 狀態(tài)為失?。╮ejected)時(shí)立即調(diào)用
fail: failDeferred.done,
// 使用指定的上下文和參數(shù)執(zhí)行失敗回調(diào)函數(shù)隊(duì)列
// 通過調(diào)用failDeferred.rejectWith()實(shí)現(xiàn)
rejectWith: failDeferred.resolveWith,
// 調(diào)用失敗回調(diào)函數(shù)隊(duì)列
// 通過調(diào)用failDeferred.resolve()實(shí)現(xiàn)
reject: failDeferred.resolve,
// 判斷狀態(tài)是否為成功(resolved)
isRejected: failDeferred.isResolved,
// 每次調(diào)用回調(diào)函數(shù)之前先調(diào)用傳入的成功過濾函數(shù)或失敗過濾函數(shù),并將過濾函數(shù)的返回值作為回調(diào)函數(shù)的參數(shù)
// 最終返回一個(gè)只讀視圖(調(diào)用promise實(shí)現(xiàn))
// fnDone在狀態(tài)是否為成功(resolved)時(shí)被調(diào)用
// fnFail在狀態(tài)是否為失?。╮ejected)時(shí)被調(diào)用
// 關(guān)于其他的解釋:
// 1. 有的文章翻譯為“管道機(jī)制”,從字面無法理解要表達(dá)什么含義,因此至少是不準(zhǔn)確
// 2. 錯(cuò)誤理解:所謂的pipe,只是把傳入的fnDone和fnFail放到了成功隊(duì)列和失敗隊(duì)列的數(shù)組頭部
pipe: function( fnDone, fnFail ) {
return jQuery.Deferred(function( newDefer ) {
jQuery.each( {
done: [ fnDone, "resolve" ], // done在后文中會(huì)指向deferred.done
fail: [ fnFail, "reject" ]
}, function( handler, data ) {
var fn = data[ 0 ],
action = data[ 1 ],
returned;
if ( jQuery.isFunction( fn ) ) {
deferred[ handler ](function() {
returned = fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise().then( newDefer.resolve, newDefer.reject );
} else {
newDefer[ action ]( returned );
}
});
} else {
deferred[ handler ]( newDefer[ action ] );
}
});
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
// 返回的是一個(gè)不完整的Deferred的接口,沒有resolve和reject,即不能 修改Deferred對象的狀態(tài),
// 這是為了不讓外部函數(shù)提早觸發(fā)回調(diào)函數(shù),可以看作是一種只讀視圖。
//
// 比如$.ajax在1.5版本后不再返回XMLHttpRequest,而是返回一個(gè)封裝了 XMLHttpRequest和Deferred對象接口的object。
// 其中Deferred部分就是promise()得到 的,這樣不讓外部函數(shù)調(diào)用resolve和reject,防止在ajax完成前觸發(fā)回調(diào)函數(shù)。
// 把這兩個(gè)函數(shù)的調(diào)用權(quán)限保留給ajax內(nèi)部。
promise: function( obj ) {
if ( obj == null ) {
// 實(shí)際只會(huì)執(zhí)行一次promise,第一次執(zhí)行的結(jié)果被存儲(chǔ)在promise變量中
if ( promise ) {
return promise;
}
promise = obj = {};
}
var i = promiseMethods.length;
// 又一種循環(huán)遍歷方式
// 我習(xí)慣用:
// for( i = 0; i < len; i++ ) 或 for( i = len-1; i >=0; i-- ) 或 for( i = len; i--; )
// jQuery真是遍地是寶!
while( i-- ) {
obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
}
return obj;
}
});
// Make sure only one callback list will be used
// 成功隊(duì)列執(zhí)行完成后,會(huì)執(zhí)行失敗帶列的取消方法
// 失敗隊(duì)列執(zhí)行完成后,會(huì)執(zhí)行成功隊(duì)列的取消方法
// 確保只有一個(gè)函數(shù)隊(duì)列會(huì)被執(zhí)行,即要么執(zhí)行成功隊(duì)列,要么執(zhí)行失敗隊(duì)列;
// 即狀態(tài)只能是或成功、或失敗,無交叉調(diào)用
// deferred和failDeferred的canceled屬性,只能通過閉包引用,因此不用擔(dān)心狀態(tài)、上下文的混亂
deferred.done( failDeferred.cancel ).fail( deferred.cancel );
// Unexpose cancel
// 隱藏cancel接口,即無法從外部取消成功函數(shù)隊(duì)列
delete deferred.cancel;
// Call given func if any
// 執(zhí)行傳入的func函數(shù)
if ( func ) {
func.call( deferred, deferred );
}
return deferred;
}

5.5 jQuery.when
復(fù)制代碼 代碼如下:

// Deferred helper
// 異步隊(duì)列工具函數(shù)
// firstParam:一個(gè)或多個(gè)Deferred對象或JavaScript普通對象
when: function( firstParam ) {
var args = arguments,
i = 0,
length = args.length,
count = length,
// 如果arguments.length等于1,并且firstParam是Deferred,則deferred=firstParam
// 否則創(chuàng)建一個(gè)新的Deferred對象(如果arguments.length等于0或大于1,則創(chuàng)建一個(gè)新的Deferred對象)
// 通過jQuery.isFunction( firstParam.promise )簡單的判斷是否是Deferred對象
deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
firstParam :
jQuery.Deferred();
// 構(gòu)造成功(resolve)回調(diào)函數(shù)
function resolveFunc( i ) {
return function( value ) {
// 如果傳入的參數(shù)大于一個(gè),則將傳入的參數(shù)轉(zhuǎn)換為真正的數(shù)組(arguments沒有slice方法,借雞生蛋)
args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
if ( !( --count ) ) {
// Strange bug in FF4:
// Values changed onto the arguments object sometimes end up as undefined values
// outside the $.when method. Cloning the object into a fresh array solves the issue
// 執(zhí)行成功回調(diào)函數(shù)隊(duì)列,上下文強(qiáng)制為傳入的第一個(gè)Deferred對象
deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
}
};
}
// 如果參數(shù)多于一個(gè)
if ( length > 1 ) {
for( ; i < length; i++ ) {
// 簡單的判斷是否是Deferred對象,是則調(diào)用.promise().then(),否則忽略
if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
// 增加成功回調(diào)函數(shù)和失敗回調(diào)函數(shù)到各自的隊(duì)列中
args[ i ].promise().then( resolveFunc(i), deferred.reject );
} else {
// 計(jì)數(shù)器,表示發(fā)現(xiàn)不是Deferred對象,而是普通JavaScript對象
--count;
}
}
// 計(jì)數(shù)器為0時(shí),表示傳入的參數(shù)都不是Deferred對象
// 執(zhí)行成功回調(diào)函數(shù)隊(duì)列,上下文強(qiáng)制為傳入的第一個(gè)Deferred對象
if ( !count ) {
deferred.resolveWith( deferred, args );
}
// deferred !== firstParam,即deferred為新創(chuàng)建的Deferred對象
// 即length == 0
} else if ( deferred !== firstParam ) {
// 執(zhí)行成功回調(diào)函數(shù)隊(duì)列,上下文強(qiáng)制為新創(chuàng)建的Deferred對象
deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
}
// 返回傳入的第一個(gè)Deferred或新創(chuàng)建的Deferred對象的只讀視圖
return deferred.promise();
}


5.6 Deferred應(yīng)用
l jQuery.ajax()
n TODO
5.7 可以學(xué)習(xí)的技巧
l 閉包
復(fù)制代碼 代碼如下:

function a(){
var guid = 1;
return function(){
return guid++;
}
}
var defer = a();
console.info( defer() ); // 1
console.info( defer() ); // 2
console.info( defer() ); // 3
console.info( defer() ); // 4

l 避免null、undefined造成ReferenceError的常見技巧
args = args || [];
l 遍歷動(dòng)態(tài)數(shù)組的技巧
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );
}
l try/catch/finally 實(shí)現(xiàn)錯(cuò)誤處理
語法
說明
try {
// tryStatements
} catch( exception ) {
// catchStatements
} finally {
// finallyStatements
}
tryStatements
必選項(xiàng)。
可能發(fā)生錯(cuò)誤的語句。
exception
必選項(xiàng)。任何變量名。
exception 的初始化值是扔出的錯(cuò)誤的值。
catchStatements
可選項(xiàng)。
處理在相關(guān)聯(lián)的 tryStatement 中發(fā)生的錯(cuò)誤的語句。
finallyStatements
可選項(xiàng)。
在所有其他過程發(fā)生之后無條件執(zhí)行的語句。
l 鏈?zhǔn)綄ο螅和ㄟ^返回this實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
方法
返回值
done
this(即deferred)
resolveWith
this(即deferred)
resolve
this(即deferred)
cancel
this(即deferred)
l 代碼復(fù)用 $.each
jQuery.each( {
done: [ fnDone, "resolve" ], // done在后文中會(huì)指向deferred.done
fail: [ fnFail, "reject" ]
}, function( handler, data ) {
// 公共代碼復(fù)用
});
5.8 后續(xù)
l Deferred在jQuery中的應(yīng)用
l Deferred的自定義應(yīng)用

相關(guān)文章

最新評論