上文我們已引見了ajax
的幾個副函數ajaxPrefilter
和ajaxTransport
,本文重要引見ajax
主函數的內部完成
我們平常挪用ajax
有三種寫法
// 第一種寫法
$.ajax({
url:url,
data:{...},
...
success:function(){},
error:function(){}
})
// 第二種寫法
$.ajax(url, {
data:{...},
...
success:function(){},
error:function(){}
})
// 第三種寫法,也就是deferred的寫法
$.ajax(url, {
data:{...},
...
}).done().fail();
第一種和第二種僅僅是url
的位置差別,ajax
內部會推斷傳入ajax
的第一個參數是不是是對象來舉行推斷
// If url is an object, simulate pre-1.5 signature
// 運用 $.ajax({url:url,data:data}) 的寫法,須要轉換成 $.ajax(url, {data:data}); 的寫法
if ( typeof url === "object" ) {
options = url;
url = undefined;
}
ajax
內部經由過程新增jqXHR
對象來增添ajax
的功用,比方statusCode
依據ajax
中設置相應的http
狀況碼對象的函數來完成當相應的狀況碼對應到設置的狀況碼時,觸發相應的函數
// Fake xhr
// 模仿出來的 ajax ,增添原生ajax的功用
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function( key ) {},
// Raw string
getAllResponseHeaders: function() {},
// Caches the header
setRequestHeader: function( name, value ) { },
// Overrides response content-type header
overrideMimeType: function( type ) {},
// Status-dependent callbacks
// 狀況碼對應后,怎樣觸發相干的函數
statusCode: function( map ) {},
// Cancel the request
// ajax時刻凌駕timeout相應的時刻時,就觸發 abort 函數
abort: function( statusText ) {}
};
而ajax
的第三種寫法,就是經由過程將jqXHR
添加到deferred
中,所以deferred
的一切要領,ajax
也可以一樣運用
// Deferreds
// ajax 可以運用兩種體式格局來寫,鏈式挪用的話,就須要運用 deferreds ,如許就可以觸發是不是是 done照樣其他狀況來觸發相應的事宜
deferred = jQuery.Deferred(),
completeDeferred = jQuery.Callbacks("once memory"),
// Attach deferreds
// 把deferred的要領,添加到 模仿的 jqXHR 中
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
上一文中引見了ajax
的addPrefilters
函數,在ajax
發送數據之前,會經由過程inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
挪用一切的預處置懲罰函數,發送數據時,經由過程transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
來發送數據,transports
內會依據是不是跨域挑選哪一個send/abort
函數,返回的效果,經由過程done()
函數舉行處置懲罰
// Main method
ajax: function( url, options ) {
// If url is an object, simulate pre-1.5 signature
// 運用 $.ajax({url:url,data:data}) 的寫法,須要轉換成 $.ajax(url, {data:data}); 的寫法
if ( typeof url === "object" ) {
options = url;
url = undefined;
}
// Force options to be an object
options = options || {};
var transport,
// URL without anti-cache param
cacheURL,
// Response headers
responseHeadersString,
responseHeaders,
// timeout handle
timeoutTimer,
// Cross-domain detection vars
parts,
// To know if global events are to be dispatched
fireGlobals,
// Loop variable
i,
// Create the final options object
s = jQuery.ajaxSetup( {}, options ), // 經由過程把options的參數放到一個新對象中,如許就一切的參數都只會影響當前的ajax
// Callbacks context
callbackContext = s.context || s, // 實行的上下文,依據定名,應該是回調函數的上下文
// Context for global events is callbackContext if it is a DOM node or jQuery collection
// 這裏應該是觸發 ajax 的全局事宜,假如有設置上下文context,那末就可以運用 jQuery( callbackContext ) ,也就是某個元夙來綁定
// 不然,就運用默許的 jQuery底層的event來挪用
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
jQuery( callbackContext ) :
jQuery.event,
// Deferreds
// ajax 可以運用兩種體式格局來寫,鏈式挪用的話,就須要運用 deferreds ,如許就可以觸發是不是是 done照樣其他狀況來觸發相應的事宜
deferred = jQuery.Deferred(),
completeDeferred = jQuery.Callbacks("once memory"),
// Status-dependent callbacks
// 狀況碼對應的回調操縱,和success 事宜同級別 statusCode: {404:function(){alert('頁面不存在')}
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
// Fake xhr
// 模仿出來的 ajax ,原生的還不夠壯大
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function( key ) {
var match;
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
while ( (match = rheaders.exec( responseHeadersString )) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
match = responseHeaders[ key.toLowerCase() ];
}
return match == null ? null : match;
},
// Raw string
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
},
// Caches the header
setRequestHeader: function( name, value ) {
var lname = name.toLowerCase();
if ( !state ) {
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
requestHeaders[ name ] = value;
}
return this;
},
// Overrides response content-type header
overrideMimeType: function( type ) {
if ( !state ) {
s.mimeType = type;
}
return this;
},
// Status-dependent callbacks
// 狀況碼對應后,怎樣觸發相干的函數
statusCode: function( map ) {
var code;
if ( map ) {
if ( state < 2 ) {
for ( code in map ) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
}
} else {
// Execute the appropriate callbacks
jqXHR.always( map[ jqXHR.status ] );
}
}
return this;
},
// Cancel the request
// ajax時刻凌駕timeout相應的時刻時,就觸發 abort 函數
abort: function( statusText ) {
var finalText = statusText || strAbort;
if ( transport ) {
transport.abort( finalText );
}
done( 0, finalText );
return this;
}
};
// Attach deferreds
// 把deferred的要領,添加到 模仿的 jqXHR 中
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
// Remove hash character (#7531: and string promotion)
// Add protocol if not provided (prefilters might expect it)
// Handle falsy url in the settings object (#10093: consistency with old signature)
// We also use the url parameter if available
// 把地點中 hash 設置為空,由於在ajax中,hash值是沒有用途的,假如地點中有//,就替換成 http:// 如許的規範寫法
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
.replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); // 假如地點寫成 //data.php 就會轉換成 http://data.php
// Alias method option to type as per ticket #12004
// get/post 也可以運用 method 或者是 type
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
// text html json 假如如許寫 dataType,就須要轉換成一個鳩合
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
// A cross-domain request is in order when we have a protocol:host:port mismatch
// 檢測是不是跨域
if ( s.crossDomain == null ) {
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" ) ) )
);
}
// Convert data if not already a string
// 處置懲罰運用 ajax 通報的數據
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
// Apply prefilters,
// 這時刻,觸發一切的預處置懲罰函數
// s 就是 ajax 一切的參數, options 開發者傳進來的參數
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
// If request was aborted inside a prefilter, stop there
// 假如在 prefilter 內里的要求就被住手了,就放回當前對象
if ( state === 2 ) {
return jqXHR;
}
// We can fire global events as of now if asked to
fireGlobals = s.global;
// Watch for a new set of requests
// 全局事宜觸發,這時刻候沒有運用詳細的元素,直接運用的默許是 document 觸發 $(document).on("ajaxStart",function(){}),而不是$("div").on("ajaxStart",function(){})
if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger("ajaxStart");
}
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
// rnoContent 為 get/head ,就是運用 ajax 的時刻,把數據加到網址的背面
s.hasContent = !rnoContent.test( s.type );
// Save the URL in case we're toying with the If-Modified-Since
// and/or If-None-Match header later on
cacheURL = s.url;
// More options handling for requests with no content
if ( !s.hasContent ) {
// If data is available, append data to url
if ( s.data ) {
cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Add anti-cache in url if needed
// 假如不須要緩存,就會在url的背面加上時刻戳,如許每次要求都是一次新的要求
if ( s.cache === false ) {
s.url = rts.test( cacheURL ) ?
// If there is already a '_' parameter, set its value
cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
// Otherwise add one to the end
cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
// 假如數據沒有變,就運用緩存的數據
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 ] );
}
}
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
jqXHR.setRequestHeader( "Content-Type", s.contentType );
}
// Set the Accepts header for the server, depending on the dataType
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[ "*" ]
);
// Check for headers option
for ( i in s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
}
// Allow custom headers/mimetypes and early abort
// 假如住手 ajax 事宜,就直接挪用 abort 事宜
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
// Abort if not done already and return
return jqXHR.abort();
}
// aborting is no longer a cancellation
strAbort = "abort";
// Install callbacks on deferreds
// 把 success 這些事宜映射到 jqXHR 中
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
}
// Get transport
// 觸發還調,ajax對象的send/abort要領,假如跨域,就在url內建立script的要領,假如不跨域,就運用經由過程原生ajax封裝好的send/abort要領
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
// If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
jqXHR.readyState = 1;
// Send global event
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
// Timeout
// 假如要求的時刻大於設置的timeout,就挪用 abort 函數
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout );
}
try {
state = 1;
// 挪用transport封裝好的 send 發送
transport.send( requestHeaders, done );
} catch ( e ) {
// Propagate exception as error if not done
if ( state < 2 ) {
done( -1, e );
// Simply rethrow otherwise
} else {
throw e;
}
}
}
// Callback for when everything is done
function done( status, nativeStatusText, responses, headers ) {
var isSuccess, success, error, response, modified,
statusText = nativeStatusText;
// Called once
if ( state === 2 ) {
return;
}
// State is "done" now
state = 2;
// Clear timeout if it exists
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
// Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
responseHeadersString = headers || "";
// Set readyState
jqXHR.readyState = status > 0 ? 4 : 0;
// Determine if successful
isSuccess = status >= 200 && status < 300 || status === 304;
// Get response data
// 然後jquery舉行處置懲罰,獲得適宜的輸出,可以經由過程運用 console.log來打印出 ajax 返回的數據,來看出是怎樣處置懲罰的數據
// console.log(responses)
if ( responses ) {
response = ajaxHandleResponses( s, jqXHR, responses );
}
// console.log(responses)
// Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert( s, response, jqXHR, isSuccess );
// console.log(responses)
// If successful, handle type chaining
if ( isSuccess ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
// 是不是須要運用緩存中的數據
if ( s.ifModified ) {
modified = jqXHR.getResponseHeader("Last-Modified");
if ( modified ) {
jQuery.lastModified[ cacheURL ] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if ( modified ) {
jQuery.etag[ cacheURL ] = modified;
}
}
// if no content
if ( status === 204 || s.type === "HEAD" ) {
statusText = "nocontent";
// if not modified
} else if ( status === 304 ) {
statusText = "notmodified";
// If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
if ( status || !statusText ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
}
}
}
// Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
// Success/Error
// 經由過程返回的數據是不是勝利,來觸發deferred 的resolveWith和rejectWith函數
if ( isSuccess ) {
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
// Status-dependent callbacks
// 挪用ajax返回的狀況碼,來觸發開發者本身設置的http狀況碼的函數,
jqXHR.statusCode( statusCode );
statusCode = undefined;
// 假如設置了全局函數,依據ajax是不是挪用勝利,來挑選觸發ajaxSuccess函數照樣ajaxError函數
if ( fireGlobals ) {
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
[ jqXHR, s, isSuccess ? success : error ] );
}
// Complete
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
// ajax 挪用完成,觸發全局函數 ajaxComplete,假如當前頁悉數的ajax都挪用完成,就觸發全局函數ajaxStop
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
// Handle the global AJAX counter
if ( !( --jQuery.active ) ) {
jQuery.event.trigger("ajaxStop");
}
}
}
// 全部 ajax 挪用完后,返回的是 jqXHR 對象,就可以運用deferred的一系列要領 done,fail 等要領了
return jqXHR;
},
待續…