由于jQuery ajax對(duì)Callbacks、Deferred、serialize、event等模塊的依賴,建議對(duì)這些模塊沒有認(rèn)識(shí)的朋友看一下 jQuery Callbacks 、 jQuery Deferred 、 jQuery serialize 、 jQuery event(上) 、 jQuery event(下) 。
這篇文章主要分析的是擁有380+行的jQuery.ajax函數(shù),該函數(shù)是jQuery ajax的核心函數(shù),jQuery的其他ajax方法幾乎都是基于該方法的。
上一篇文章我們了解了Baidu ajax(當(dāng)然是舊版的,還是被簡化的……),那么我們想給這個(gè)簡單的ajax方法添加什么功能呢?
?
可鏈?zhǔn)讲僮?
既然是jQuery必然先要解決的是鏈?zhǔn)讲僮鞯膯栴}。
jQuery中的Deferred可以實(shí)現(xiàn)異步鏈?zhǔn)侥P蛯?shí)現(xiàn),Promise對(duì)象可以輕易的綁定成功、失敗、進(jìn)行中三種狀態(tài)的回調(diào)函數(shù),然后通過在狀態(tài)碼在來回調(diào)不同的函數(shù)就行了。
?
但僅僅返回一個(gè)promise沒什么用
promise只能處理異步,我們需要返回一個(gè)更有用的東西。
jqXHR就是這樣的東西,實(shí)際上他是一個(gè)山寨版的XHR對(duì)象。
這樣我們就可以擴(kuò)展一下XHR對(duì)象的功能, 提供一定的容錯(cuò)處理,暴露給外部提供一些方法。
//
贗品xhr,或者說山寨xhr……╮(╯▽╰)╭
//
為了能夠?qū)崿F(xiàn)鏈?zhǔn)讲僮?
//
順便擴(kuò)展一下xhr對(duì)象功能
//
還有提供一定的容錯(cuò)處理
jqXHR =
{
//
準(zhǔn)備狀態(tài)
readyState: 0
,
//
如果需要,創(chuàng)建一個(gè)響應(yīng)頭參數(shù)的表
getResponseHeader:
function
( key ) {
var
match;
//
如果狀態(tài)為2,狀態(tài)2表示ajax完成
if
( state === 2
) {
//
如果沒有相應(yīng)頭
if
( !
responseHeaders ) {
//
相應(yīng)頭設(shè)空
responseHeaders =
{};
while
( (match =
rheaders.exec( responseHeadersString )) ) {
//
組裝相應(yīng)頭
responseHeaders[ match[1].toLowerCase() ] = match[ 2
];
}
}
//
響應(yīng)頭對(duì)應(yīng)的key的值
match =
responseHeaders[ key.toLowerCase() ];
}
//
返回
return
match ==
null
?
null
: match;
},
//
返回響應(yīng)頭字符串
getAllResponseHeaders:
function
() {
//
看看是否接收到了,接收到直接返回,否則為null
return
state === 2 ? responseHeadersString :
null
;
},
//
設(shè)置請(qǐng)求頭
setRequestHeader:
function
( name, value ) {
var
lname =
name.toLowerCase();
//
如果state不為0
if
( !
state ) {
//
如果requestHeadersNames[ lname ]不為空,
//
則requestHeadersNames[ lname ]不變,name設(shè)置為該值
//
否則,requestHeadersNames[ lname ]不空,
//
則requestHeadersNames[ lname ]設(shè)置為name,
//
該映射關(guān)系用于避免用戶大小寫書寫錯(cuò)誤之類的問題,容錯(cuò)處理
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] ||
name;
//
現(xiàn)在的name是對(duì)的,或者是第一次設(shè)置這個(gè)name,不需要容錯(cuò)
//
設(shè)置請(qǐng)求頭對(duì)應(yīng)值
requestHeaders[ name ] =
value;
}
return
this
;
},
//
重寫相應(yīng)頭content-type
overrideMimeType:
function
( type ) {
if
( !
state ) {
s.mimeType
=
type;
}
return
this
;
},
//
對(duì)應(yīng)狀態(tài)的回調(diào)函數(shù)集
statusCode:
function
( map ) {
var
code;
//
如果map存在,準(zhǔn)備組裝
if
( map ) {
//
如果狀態(tài)小于2,表示舊的回調(diào)可能還沒有用到
if
( state < 2
) {
//
遍歷map里面的所有code
for
( code
in
map ) {
//
用類似鏈表的方式添加,以保證舊的回調(diào)依然存在
statusCode[ code ] =
[ statusCode[ code ], map[ code ] ];
}
//
狀態(tài)大于2,證明已經(jīng)完成了
}
else
{
//
無論Deferred成功還是失敗都執(zhí)行當(dāng)前狀態(tài)回調(diào)
jqXHR.always( map[ jqXHR.status ] );
}
}
return
this
;
},
//
中斷請(qǐng)求
abort:
function
( statusText ) {
var
finalText = statusText ||
strAbort;
//
可以先理解成XHR對(duì)象,當(dāng)然這也不是真正的XHR對(duì)象
if
( transport ) {
transport.abort( finalText );
}
//
調(diào)用done,表示干完了
done( 0
, finalText );
return
this
;
}
};
?
可是這還沒有鏈?zhǔn)桨。?
怎么把jqXHR變成一個(gè)Promise呢?
讓一個(gè)對(duì)象擁有另一個(gè)對(duì)象的方法,大家會(huì)想到什么呢?
繼承?
不,直接插上去就好了……這里就體現(xiàn)了Javascript“易插拔”的特點(diǎn)……自己亂起的……囧rz……
應(yīng)該說動(dòng)態(tài)、弱類的特點(diǎn)。
什么意思?就是將Promise上的方法插到j(luò)qXHR對(duì)象上就行了。
//
在jqXHR粘上promise的所有方法,此時(shí)jqXHR就很像一個(gè)promise了
//
實(shí)際上就是用jQuery.extend(jqXHR, promise)而已
//
jqXHR剛山寨了xhr對(duì)象,又開始山寨promise對(duì)象了
//
順便把jqXHR的complete綁上completeDeferred.add
//
意思是jqXHR狀態(tài)完成后調(diào)用completeDeferred這個(gè)Callbacks列隊(duì)
//
話說promise里面并沒有complete這個(gè)方法
//
后面我們可以看到,作者大人又要給jqXHR插上complete、success、error方法
//
Javascript就是這樣簡單,即插即用……囧rz
deferred.promise( jqXHR ).complete =
completeDeferred.add;
//
綁定jqXHR.success為promise里面的done方法
jqXHR.success =
jqXHR.done;
//
綁定jqXHR.error為promise里面的fail方法
jqXHR.error = jqXHR.fail;
這樣子直接插上去在強(qiáng)類語言中是做不到的,當(dāng)然也可以用組合模式來實(shí)現(xiàn)一個(gè)對(duì)象擁有多個(gè)對(duì)象的方法,但是卻無法動(dòng)態(tài)的去給對(duì)象添加各種方法。
強(qiáng)類語言會(huì)限制對(duì)象一輩子只能“會(huì)”那么幾種“方法”,Javascript的對(duì)象可以在生命周期內(nèi)隨意“學(xué)習(xí)”各種“方法”。
所以說,強(qiáng)類語言和Javascript的對(duì)象模型是有差別的,將設(shè)計(jì)模式生搬硬套進(jìn)Javascript或許是錯(cuò)誤的。
我們?cè)倥e個(gè)例子,比如如果用所謂的Javascript組合模式來重寫上面的代碼可能會(huì)是這樣的:
// 定義局部變量deferred和completeDeferred function JQXHR(){ // 定義jqXHR的屬性和方法 for (i in deferred.promise){ this [i] = this .promise[i]; } this .complete = completeDeferred.add; this .success = this .done; this .error = this .done } var jqXHR = new JQXHR();大家覺得哪個(gè)好呢?
變成上面的樣子主要是因?yàn)橐鉀Q下面兩個(gè)問題:
- jqXHR由于是暴露在外的,他不能包含deferred和completeDeferred,不允許用戶在外部操作這兩個(gè)對(duì)象的狀態(tài)。
- deferred和completeDeferred也不能成為jqXHR的私有變量,因?yàn)檫€要更具ajax事件觸發(fā)。
?
提供全局事件,使得UI可以根據(jù)ajax狀態(tài)進(jìn)行改變
這里主要利用了jQuery.event.trigger和jQuery.fn.trigger模擬發(fā)事件。
//
如果需要,而且全局事件沒有被觸發(fā)過
if
( fireGlobals && jQuery.active++ === 0
) {
//
則通過jQuery.event.trigger模擬觸發(fā)
jQuery.event.trigger("ajaxStart"
);
}
當(dāng)ajax開始時(shí)模擬全局事件,ajaxStart。
//
如果需要,對(duì)特定對(duì)象觸發(fā)全局事件ajaxSend
if
( fireGlobals ) {
globalEventContext.trigger(
"ajaxSend"
, [ jqXHR, s ] );
}
ajax發(fā)送消息,觸發(fā)ajaxSend。
//
如果需要觸發(fā)全局事件
if
( fireGlobals ) {
//
對(duì)指定元素觸發(fā)事件ajaxSuccess或者ajaxError
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError"
,
[ jqXHR, s, isSuccess
?
success : error ] );
}
//
回調(diào)完成后的Callbacks隊(duì)列
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
//
如果需要觸發(fā)全局事件
if
( fireGlobals ) {
//
對(duì)指定元素觸發(fā)事件ajaxComplete
globalEventContext.trigger( "ajaxComplete"
, [ jqXHR, s ] );
//
該ajax觸發(fā)完畢,標(biāo)記active減1,如果為0,證明所有ajax結(jié)束
if
( !( --
jQuery.active ) ) {
//
觸發(fā)ajaxStop事件
jQuery.event.trigger("ajaxStop"
);
}
}
結(jié)束時(shí)候觸發(fā)ajaxSuccess或ajaxError,再出發(fā)ajaxComplete,如果全部ajax結(jié)束則觸發(fā)ajaxStop。
?
?
是否允許使用緩存數(shù)據(jù)
//
如果不需要content
//
看看需不需要加其他信息
if
( !
s.hasContent ) {
//
如果data存在,那么已經(jīng)序列化
if
( s.data ) {
//
添加到cacheURL中
cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) +
s.data );
//
刪除掉則后面就不會(huì)被發(fā)送出去了
delete
s.data;
}
//
看看是否需要避免數(shù)據(jù)從緩存中讀取
if
( s.cache ===
false
) {
s.url
= rts.test( cacheURL ) ?
//
如果已經(jīng)有_參數(shù),那么設(shè)置他的值
cacheURL.replace( rts, "$1_=" + ajax_nonce++
) :
//
否則添加一個(gè)_ = xxx在URL后面
cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++
;
}
}
通過給地址附加參數(shù)_=xxx來避免緩存。
//
看看需不需要設(shè)置If-Modified-Since和If-None-Match頭信息
if
( s.ifModified ) {
if
( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader(
"If-Modified-Since"
, jQuery.lastModified[ cacheURL ] );
}
if
( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader(
"If-None-Match"
, jQuery.etag[ cacheURL ] );
}
}
以及設(shè)置If-Modified-Since和If-None-Match頭信息。
//
看看是否需要緩存置If-Modified-Since和If-None-Match頭
if
( s.ifModified ) {
//
取得Last-Modified
modified = jqXHR.getResponseHeader("Last-Modified"
);
//
如果Last-Modified存在
if
( modified ) {
//
在jQuery.lastModified[cacheURL]保存Last-Modified
jQuery.lastModified[ cacheURL ] =
modified;
}
//
取得etag
modified = jqXHR.getResponseHeader("etag"
);
//
如果etag存在
if
( modified ) {
//
在jQuery.etag[cacheURL]緩存etag
jQuery.etag[ cacheURL ] =
modified;
}
}
緩存 If-Modified-Since和If-None-Match頭信息。
?
設(shè)置超時(shí)
//
如果是異步,并且設(shè)置了超時(shí)
if
( s.async && s.timeout > 0
) {
//
設(shè)置超時(shí)
timeoutTimer = setTimeout(
function
() {
jqXHR.abort(
"timeout"
);
}, s.timeout );
}
主要功能分析完了,完整備注代碼見下。?
?
完整備注
jQuery.ajax =
function
( url, options ) {
//
如果url是一個(gè)obj,模擬1.5版本以前的方法
if
(
typeof
url === "object"
) {
options
=
url;
url
=
undefined;
}
//
設(shè)置options
options = options ||
{};
var
transport,
//
緩存cacheURL
cacheURL,
//
響應(yīng)頭
responseHeadersString,
responseHeaders,
//
超時(shí)控制器
timeoutTimer,
//
跨域判定變量
parts,
//
是否需要觸發(fā)全局事件
fireGlobals,
//
循環(huán)變量
i,
//
通過jQuery.ajaxSetup改造參數(shù)對(duì)象
s =
jQuery.ajaxSetup( {}, options ),
//
回調(diào)指定上下文,也就是他的this
callbackContext = s.context ||
s,
//
全局事件中的相應(yīng)函數(shù)的指定上下文
//
有s.context,且是DOM節(jié)點(diǎn),或者jQuery收集器
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
//
通過jQuery包裝
jQuery( callbackContext ) :
//
否則為jQuery.event
jQuery.event,
//
新建一個(gè)deferred
deferred =
jQuery.Deferred(),
//
deferred完成后的Callbacks隊(duì)列
completeDeferred = jQuery.Callbacks("once memory"
),
//
對(duì)應(yīng)狀態(tài)的回調(diào)函數(shù)集
statusCode = s.statusCode ||
{},
//
請(qǐng)求頭
requestHeaders =
{},
requestHeadersNames
=
{},
//
包裝類jqXHR的狀態(tài)
state = 0
,
//
默認(rèn)中斷消息
strAbort = "canceled"
,
//
贗品xhr,或者說山寨xhr……╮(╯▽╰)╭
//
為了能夠?qū)崿F(xiàn)鏈?zhǔn)讲僮?
//
順便擴(kuò)展一下xhr對(duì)象功能
//
還有提供一定的容錯(cuò)處理
jqXHR =
{
//
準(zhǔn)備狀態(tài)
readyState: 0
,
//
如果需要,創(chuàng)建一個(gè)響應(yīng)頭參數(shù)的表
getResponseHeader:
function
( key ) {
var
match;
//
如果狀態(tài)為2,狀態(tài)2表示ajax完成
if
( state === 2
) {
//
如果沒有相應(yīng)頭
if
( !
responseHeaders ) {
//
相應(yīng)頭設(shè)空
responseHeaders =
{};
while
( (match =
rheaders.exec( responseHeadersString )) ) {
//
組裝相應(yīng)頭
responseHeaders[ match[1].toLowerCase() ] = match[ 2
];
}
}
//
響應(yīng)頭對(duì)應(yīng)的key的值
match =
responseHeaders[ key.toLowerCase() ];
}
//
返回
return
match ==
null
?
null
: match;
},
//
返回響應(yīng)頭字符串
getAllResponseHeaders:
function
() {
//
看看是否接收到了,接收到直接返回,否則為null
return
state === 2 ? responseHeadersString :
null
;
},
//
緩存請(qǐng)求頭
setRequestHeader:
function
( name, value ) {
var
lname =
name.toLowerCase();
//
如果state不為0
if
( !
state ) {
//
如果requestHeadersNames[ lname ]不為空,
//
則requestHeadersNames[ lname ]不變,name設(shè)置為該值
//
否則,requestHeadersNames[ lname ]不空,
//
則requestHeadersNames[ lname ]設(shè)置為name,
//
該映射關(guān)系用于避免用戶大小寫書寫錯(cuò)誤之類的問題,容錯(cuò)處理
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] ||
name;
//
現(xiàn)在的name是對(duì)的,或者是第一次設(shè)置這個(gè)name,不需要容錯(cuò)
//
設(shè)置請(qǐng)求頭對(duì)應(yīng)值
requestHeaders[ name ] =
value;
}
return
this
;
},
//
重寫相應(yīng)頭content-type
overrideMimeType:
function
( type ) {
if
( !
state ) {
s.mimeType
=
type;
}
return
this
;
},
//
對(duì)應(yīng)狀態(tài)的回調(diào)函數(shù)集
statusCode:
function
( map ) {
var
code;
//
如果map存在,準(zhǔn)備組裝
if
( map ) {
//
如果狀態(tài)小于2,表示舊的回調(diào)可能還沒有用到
if
( state < 2
) {
//
遍歷map里面的所有code
for
( code
in
map ) {
//
用類似鏈表的方式添加,以保證舊的回調(diào)依然存在
statusCode[ code ] =
[ statusCode[ code ], map[ code ] ];
}
//
狀態(tài)大于2,證明已經(jīng)完成了
}
else
{
//
無論Deferred成功還是失敗都執(zhí)行當(dāng)前狀態(tài)回調(diào)
jqXHR.always( map[ jqXHR.status ] );
}
}
return
this
;
},
//
中斷請(qǐng)求
abort:
function
( statusText ) {
var
finalText = statusText ||
strAbort;
//
可以先理解成XHR對(duì)象,當(dāng)然這也不是真正的XHR對(duì)象
if
( transport ) {
transport.abort( finalText );
}
//
調(diào)用done,表示干完了
done( 0
, finalText );
return
this
;
}
};
//
在jqXHR粘上promise的所有方法,此時(shí)jqXHR就很像一個(gè)promise了
//
實(shí)際上就是用jQuery.extend(jqXHR, promise)而已
//
jqXHR剛山寨了xhr對(duì)象,又開始山寨promise對(duì)象了
//
順便把jqXHR的complete綁上completeDeferred.add
//
意思是jqXHR狀態(tài)完成后調(diào)用completeDeferred這個(gè)Callbacks列隊(duì)
//
話說promise里面并沒有complete這個(gè)方法
//
后面我們可以看到,作者大人又要給jqXHR插上complete、success、error方法
//
Javascript就是這樣簡單,即插即用……囧rz
deferred.promise( jqXHR ).complete =
completeDeferred.add;
//
綁定jqXHR.success為promise里面的done方法
jqXHR.success =
jqXHR.done;
//
綁定jqXHR.error為promise里面的fail方法
jqXHR.error =
jqXHR.fail;
//
確定url參數(shù),否則用當(dāng)前地址。將地址的#號(hào)后面的所有東西去掉
//
比如http://127.0.0.1#main,去掉這個(gè)#main
//
如果開頭是//,及數(shù)據(jù)傳輸協(xié)議沒有,那么用當(dāng)前頁面的數(shù)據(jù)傳輸協(xié)議替換
//
比如//127.0.0.1,變成http://127.0.0.1
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "http://"
);
//
定義type,向后兼容
s.type = options.method || options.type || s.method ||
s.type;
//
取出數(shù)據(jù)類型列表
//
沒有則為"*",
//
有則認(rèn)為是用空格分隔的字符串,將其變成數(shù)組
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""
];
//
當(dāng)協(xié)議、主機(jī)、端口和當(dāng)前不匹配時(shí),證明這是一個(gè)跨域請(qǐng)求
if
( s.crossDomain ==
null
) {
//
分隔當(dāng)前url
parts =
rurl.exec( s.url.toLowerCase() );
//
判定是否是跨域
s.crossDomain = !!( parts &&
( parts[
1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
( parts[
3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
( ajaxLocParts[
3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443
) ) )
);
}
//
如果data已經(jīng)是一個(gè)字符串了,那么就不用轉(zhuǎn)換了
if
( s.data && s.processData &&
typeof
s.data !== "string"
) {
//
序列化
s.data =
jQuery.param( s.data, s.traditional );
}
//
預(yù)過濾
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
//
如果請(qǐng)求被prefilter終止,則退出
if
( state === 2
) {
return
jqXHR;
}
//
看看需不需要觸發(fā)全局事件
fireGlobals =
s.global;
//
如果需要,而且全局事件沒有被觸發(fā)過
if
( fireGlobals && jQuery.active++ === 0
) {
//
則通過jQuery.event.trigger模擬觸發(fā)
jQuery.event.trigger("ajaxStart"
);
}
//
將類型大寫
s.type =
s.type.toUpperCase();
//
判斷需不需要設(shè)置content
s.hasContent = !
rnoContent.test( s.type );
//
緩存URL,用來在之后設(shè)置If-Modified-Since和If-None-Match
cacheURL =
s.url;
//
如果不需要content
//
看看需不需要加其他信息
if
( !
s.hasContent ) {
//
如果data存在,那么已經(jīng)序列化
if
( s.data ) {
//
添加到cacheURL中
cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) +
s.data );
//
刪除掉則后面就不會(huì)被發(fā)送出去了
delete
s.data;
}
//
看看是否需要避免數(shù)據(jù)從緩存中讀取
if
( s.cache ===
false
) {
s.url
= rts.test( cacheURL ) ?
//
如果已經(jīng)有_參數(shù),那么設(shè)置他的值
cacheURL.replace( rts, "$1_=" + ajax_nonce++
) :
//
否則添加一個(gè)_ = xxx在URL后面
cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++
;
}
}
//
看看需不需要設(shè)置If-Modified-Since和If-None-Match頭信息
if
( s.ifModified ) {
if
( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader(
"If-Modified-Since"
, jQuery.lastModified[ cacheURL ] );
}
if
( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader(
"If-None-Match"
, jQuery.etag[ cacheURL ] );
}
}
//
如果數(shù)據(jù)需要被發(fā)送,設(shè)置正確的頭
if
( s.data && s.hasContent && s.contentType !==
false
||
options.contentType ) {
jqXHR.setRequestHeader(
"Content-Type"
, s.contentType );
}
//
根據(jù)dataType,設(shè)置一個(gè)Accept頭
jqXHR.setRequestHeader(
"Accept"
,
s.dataTypes[
0 ] && s.accepts[ s.dataTypes[0] ] ?
s.accepts[ s.dataTypes[
0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : ""
) :
s.accepts[
"*"
]
};
//
遍歷s.headers,將其內(nèi)參數(shù)設(shè)置入請(qǐng)求頭
for
( i
in
s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
}
//
通過beforeSend檢查是否需要發(fā)送
if
( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) ===
false
|| state === 2
) ) {
//
終止
return
jqXHR.abort();
}
//
此時(shí)abort函數(shù)不是取消ajax,而是中斷了ajax
strAbort = "abort"
;
//
在jqXHR上綁定成功、錯(cuò)誤、完成回調(diào)函數(shù)
for
( i
in
{ success: 1, error: 1, complete: 1
} ) {
jqXHR[ i ]( s[ i ] );
}
//
得到transport
transport =
inspectPrefiltersOrTransports( transports, s, options, jqXHR );
//
如果沒有,自動(dòng)終止
if
( !
transport ) {
done(
-1, "No Transport"
);
}
else
{
//
否則,設(shè)置reayState為1
jqXHR.readyState = 1
;
//
如果需要,對(duì)特定對(duì)象觸發(fā)全局事件ajaxSend
if
( fireGlobals ) {
globalEventContext.trigger(
"ajaxSend"
, [ jqXHR, s ] );
}
//
如果是異步,并且設(shè)置了超時(shí)
if
( s.async && s.timeout > 0
) {
//
設(shè)置超時(shí)
timeoutTimer = setTimeout(
function
() {
jqXHR.abort(
"timeout"
);
}, s.timeout );
}
try
{
//
設(shè)置state為1
state = 1
;
//
開始發(fā)送
transport.send( requestHeaders, done );
}
catch
( e ) {
//
截獲錯(cuò)誤,如果ajax未完成
if
( state < 2
) {
done(
-1
, e );
//
完成了就直接拋出錯(cuò)誤
}
else
{
throw
e;
}
}
}
//
完成時(shí)的回調(diào)函數(shù)
function
done( status, nativeStatusText, responses, headers ) {
var
isSuccess, success, error, response, modified,
statusText
=
nativeStatusText;
//
如果已經(jīng)調(diào)用過該函數(shù),直接退出
if
( state === 2
) {
return
;
}
//
設(shè)置現(xiàn)在狀態(tài)已完成
state = 2
;
//
清除超時(shí)設(shè)置
if
( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
//
不管jqXHR對(duì)象要被用到何時(shí),
//
釋放transport的引用使得他可以先被垃圾回收
transport =
undefined;
//
緩存響應(yīng)頭
responseHeadersString = headers || ""
;
//
設(shè)置readyState
jqXHR.readyState = status > 0 ? 4 : 0
;
//
得到響應(yīng)數(shù)據(jù)
if
( responses ) {
//
通過ajaxHandleResponses處理數(shù)據(jù)
response =
ajaxHandleResponses( s, jqXHR, responses );
}
//
If successful, handle type chaining
//
如果成功
if
( status >= 200 && status < 300 || status === 304
) {
//
看看是否需要緩存If-Modified-Since和If-None-Match頭
if
( s.ifModified ) {
//
取得Last-Modified
modified = jqXHR.getResponseHeader("Last-Modified"
);
//
如果Last-Modified存在
if
( modified ) {
//
在jQuery.lastModified[cacheURL]保存Last-Modified
jQuery.lastModified[ cacheURL ] =
modified;
}
//
取得etag
modified = jqXHR.getResponseHeader("etag"
);
//
如果etag存在
if
( modified ) {
//
在jQuery.etag[cacheURL]緩存etag
jQuery.etag[ cacheURL ] =
modified;
}
}
//
如果沒有修改
if
( status === 304
) {
//
設(shè)置成功
isSuccess =
true
;
//
設(shè)置狀態(tài)為notmodified
statusText = "notmodified"
;
//
否則得到必要的數(shù)據(jù)
}
else
{
isSuccess
=
ajaxConvert( s, response );
statusText
=
isSuccess.state;
success
=
isSuccess.data;
error
=
isSuccess.error;
isSuccess
= !
error;
}
//
如果失敗
}
else
{
//
從statusText獲取error狀態(tài)
//
在設(shè)置statusText成"error"
//
并改變status為0
error =
statusText;
if
( status || !
statusText ) {
statusText
= "error"
;
if
( status < 0
) {
status
= 0
;
}
}
}
//
開始設(shè)置山寨xhr對(duì)象jqXHR的status和statusText
jqXHR.status =
status;
jqXHR.statusText
= ( nativeStatusText || statusText ) + ""
;
//
根據(jù)成功還是失敗,對(duì)deferred進(jìn)行回調(diào)
if
( isSuccess ) {
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
}
else
{
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
//
根據(jù)目前statusCode回調(diào)
jqXHR.statusCode( statusCode );
statusCode
=
undefined;
//
如果需要觸發(fā)全局事件
if
( fireGlobals ) {
//
對(duì)指定元素觸發(fā)事件ajaxSuccess或者ajaxError
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError"
,
[ jqXHR, s, isSuccess
?
success : error ] );
}
//
回調(diào)完成后的Callbacks隊(duì)列
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
//
如果需要觸發(fā)全局事件
if
( fireGlobals ) {
//
對(duì)指定元素觸發(fā)事件ajaxComplete
globalEventContext.trigger( "ajaxComplete"
, [ jqXHR, s ] );
//
該ajax觸發(fā)完畢,標(biāo)記active減1,如果為0,證明所有ajax結(jié)束
if
( !( --
jQuery.active ) ) {
//
觸發(fā)ajaxStop事件
jQuery.event.trigger("ajaxStop"
);
}
}
}
//
返回山寨xhr
return
jqXHR;
};
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

