由于jQuery ajax對(duì)Callbacks、Deferred、serialize、event等模塊的依賴,建議對(duì)這些模塊沒(méi)有認(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)然是舊版的,還是被簡(jiǎn)化的……),那么我們想給這個(gè)簡(jiǎn)單的ajax方法添加什么功能呢?
?
可鏈?zhǔn)讲僮?
既然是jQuery必然先要解決的是鏈?zhǔn)讲僮鞯膯?wèn)題。
jQuery中的Deferred可以實(shí)現(xiàn)異步鏈?zhǔn)侥P蛯?shí)現(xiàn),Promise對(duì)象可以輕易的綁定成功、失敗、進(jìn)行中三種狀態(tài)的回調(diào)函數(shù),然后通過(guò)在狀態(tài)碼在來(lái)回調(diào)不同的函數(shù)就行了。
?
但僅僅返回一個(gè)promise沒(méi)什么用
promise只能處理異步,我們需要返回一個(gè)更有用的東西。
jqXHR就是這樣的東西,實(shí)際上他是一個(gè)山寨版的XHR對(duì)象。
這樣我們就可以擴(kuò)展一下XHR對(duì)象的功能, 提供一定的容錯(cuò)處理,暴露給外部提供一些方法。
// 贗品xhr,或者說(shuō)山寨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 ) { // 如果沒(méi)有相應(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ò)誤之類的問(wèn)題,容錯(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)可能還沒(méi)有用到 if ( state < 2 ) { // 遍歷map里面的所有code for ( code in map ) { // 用類似鏈表的方式添加,以保證舊的回調(diào)依然存在 statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } // 狀態(tài)大于2,證明已經(jīng)完成了 } else { // 無(wú)論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 ; } };
?
可是這還沒(méi)有鏈?zhǔn)桨。?
怎么把jqXHR變成一個(gè)Promise呢?
讓一個(gè)對(duì)象擁有另一個(gè)對(duì)象的方法,大家會(huì)想到什么呢?
繼承?
不,直接插上去就好了……這里就體現(xiàn)了Javascript“易插拔”的特點(diǎn)……自己亂起的……囧rz……
應(yīng)該說(shuō)動(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ì)象,又開(kāi)始山寨promise對(duì)象了 // 順便把jqXHR的complete綁上completeDeferred.add // 意思是jqXHR狀態(tài)完成后調(diào)用completeDeferred這個(gè)Callbacks列隊(duì) // 話說(shuō)promise里面并沒(méi)有complete這個(gè)方法 // 后面我們可以看到,作者大人又要給jqXHR插上complete、success、error方法 // Javascript就是這樣簡(jiǎn)單,即插即用……囧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)類語(yǔ)言中是做不到的,當(dāng)然也可以用組合模式來(lái)實(shí)現(xiàn)一個(gè)對(duì)象擁有多個(gè)對(duì)象的方法,但是卻無(wú)法動(dòng)態(tài)的去給對(duì)象添加各種方法。
強(qiáng)類語(yǔ)言會(huì)限制對(duì)象一輩子只能“會(huì)”那么幾種“方法”,Javascript的對(duì)象可以在生命周期內(nèi)隨意“學(xué)習(xí)”各種“方法”。
所以說(shuō),強(qiáng)類語(yǔ)言和Javascript的對(duì)象模型是有差別的,將設(shè)計(jì)模式生搬硬套進(jìn)Javascript或許是錯(cuò)誤的。
我們?cè)倥e個(gè)例子,比如如果用所謂的Javascript組合模式來(lái)重寫上面的代碼可能會(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();大家覺(jué)得哪個(gè)好呢?
變成上面的樣子主要是因?yàn)橐鉀Q下面兩個(gè)問(wèn)題:
- 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ā)事件。
// 如果需要,而且全局事件沒(méi)有被觸發(fā)過(guò) if ( fireGlobals && jQuery.active++ === 0 ) { // 則通過(guò)jQuery.event.trigger模擬觸發(fā) jQuery.event.trigger("ajaxStart" ); }
當(dāng)ajax開(kāi)始時(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++ ; } }
通過(guò)給地址附加參數(shù)_=xxx來(lái)避免緩存。
// 看看需不需要設(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 ); }
主要功能分析完了,完整備注代碼見(jiàn)下。?
?
完整備注
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, // 通過(guò)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 ) ? // 通過(guò)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,或者說(shuō)山寨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 ) { // 如果沒(méi)有相應(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ò)誤之類的問(wèn)題,容錯(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)可能還沒(méi)有用到 if ( state < 2 ) { // 遍歷map里面的所有code for ( code in map ) { // 用類似鏈表的方式添加,以保證舊的回調(diào)依然存在 statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } // 狀態(tài)大于2,證明已經(jīng)完成了 } else { // 無(wú)論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ì)象,又開(kāi)始山寨promise對(duì)象了 // 順便把jqXHR的complete綁上completeDeferred.add // 意思是jqXHR狀態(tài)完成后調(diào)用completeDeferred這個(gè)Callbacks列隊(duì) // 話說(shuō)promise里面并沒(méi)有complete這個(gè)方法 // 后面我們可以看到,作者大人又要給jqXHR插上complete、success、error方法 // Javascript就是這樣簡(jiǎn)單,即插即用……囧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 // 如果開(kāi)頭是//,及數(shù)據(jù)傳輸協(xié)議沒(méi)有,那么用當(dāng)前頁(yè)面的數(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ù)類型列表 // 沒(méi)有則為"*", // 有則認(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ù)過(guò)濾 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // 如果請(qǐng)求被prefilter終止,則退出 if ( state === 2 ) { return jqXHR; } // 看看需不需要觸發(fā)全局事件 fireGlobals = s.global; // 如果需要,而且全局事件沒(méi)有被觸發(fā)過(guò) if ( fireGlobals && jQuery.active++ === 0 ) { // 則通過(guò)jQuery.event.trigger模擬觸發(fā) jQuery.event.trigger("ajaxStart" ); } // 將類型大寫 s.type = s.type.toUpperCase(); // 判斷需不需要設(shè)置content s.hasContent = ! rnoContent.test( s.type ); // 緩存URL,用來(lái)在之后設(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 ] ); } // 通過(guò)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 ); // 如果沒(méi)有,自動(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 ; // 開(kāi)始發(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)用過(guò)該函數(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 ) { // 通過(guò)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; } } // 如果沒(méi)有修改 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 ; } } } // 開(kāi)始設(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)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
