上文咱们已经介绍了ajax
的几个副函数ajaxPrefilter
和ajaxTransport
,本文主要介绍ajax
主函数的内部实现javascript
咱们通常调用ajax
有三种写法php
// 第一种写法 $.ajax({ url:url, data:{...}, ... success:function(){}, error:function(){} }) // 第二种写法 $.ajax(url, { data:{...}, ... success:function(){}, error:function(){} }) // 第三种写法,也就是deferred的写法 $.ajax(url, { data:{...}, ... }).done().fail();
第一种和第二种仅仅是url
的位置不一样,ajax
内部会判断传入ajax
的第一个参数是不是对象来进行判断html
// 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
状态码对象的函数来实现当响应的状态码对应到设置的状态码时,触发相应的函数java
// 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
也能够一样使用node
// 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()
函数进行处理jquery
// 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; },
待续...ajax