jQuery的Ajax实现封装了不同浏览器的内部实现,为外部提供了一个统一的接口,使得编写Ajax程序不再那么困难。jQuery的Ajax也支持多种格式,txt, json, jsonp, xml,以及灵活的事件机制。我们就来看看jQuery是如何封装原始的XmlHttpRequest的。
jQuery中的所有Ajax调用都是通过jQuery.ajax函数来完成的,get, post, getJSON, getScript都只是简单地将调用转发给ajax函数。因此我们就从ajax函数入手。在潜入复杂的实现之前,先考虑一下自己实现ajax会怎样做是非常有益的。显然,我们做首先创建XmlHttpRequest,然后发送异步请求,在onreadystate回调中根据请求的结果再调用自定义的回调函数(如onsuccess, oncomplte, onerror)。jQuery的实现当然要比比复杂得多,因为它不仅要处理异步请求,也要处理同步请求,对其不同格式的处理过程也不一样,但经过这番思考之后,至少心里有个底,然后在复杂的jQuery实现里不致迷失方向。先来看ajax函数的前部分:
3397 ajax: function( s ) {
3398 // Extend the settings, but re-extend 's' so that it can be
3399 // checked again later (in the test suite, specifically)
3400 s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
3401
3402 var jsonp, jsre = /=\?(&|$)/g, status, data,
3403 type = s.type.toUpperCase();
3404
3405 // convert data if not already a string
3406 if ( s.data && s.processData && typeof s.data !== "string" )
3407 s.data = jQuery.param(s.data);
3408
3409 // Handle JSONP Parameter Callbacks
3410 if ( s.dataType == "jsonp" ) {
3411 if ( type == "GET" ) {
3412 if ( !s.url.match(jsre) )
3413 s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
3414 } else if ( !s.data || !s.data.match(jsre) )
3415 s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
3416 s.dataType = "json";
3417 }
3418
3419 // Build temporary JSONP function
3420 if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
3421 jsonp = "jsonp" + jsc++;
3422
3423 // Replace the =? sequence both in the query string and the data
3424 if ( s.data )
3425 s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
3426 s.url = s.url.replace(jsre, "=" + jsonp + "$1");
3427
3428 // We need to make sure
3429 // that a JSONP style response is executed properly
3430 s.dataType = "script";
3431
3432 // Handle JSONP-style loading
3433 window[ jsonp ] = function(tmp){
3434 data = tmp;
3435 success();
3436 complete();
3437 // Garbage collect
3438 window[ jsonp ] = undefined;
3439 try{ delete window[ jsonp ]; } catch(e){}
3440 if ( head )
3441 head.removeChild( script );
3442 };
3443 }
可以看到ajax函数只接受一个参数s,它包含所有请求所需的信息,如请求方法(s.type),请求的url(s.url),请求的数据(s.data),请求返回的数据类型(s.dataType),回调函数(s.success, s.error, s.complete)等,该方法返回XmlHttpRequest对象。前面的部分主要用于处理jsonp协议,我们知道由于Ajax请求由于安全限制,不能发送跨域请求,jsonp可以解除这个限制,它并不使用XmlHttpRequest去发送请求,而是使用动态创建script元素的技术,并将它的src的元素设置成请求的url,这就要求远程返回的是JavaScript代码,而不能仅仅是数据。jsonp是这样处理的,服务器端返回的数据仍然使用json协议,但它会将数据放在一个函数调用中,函数名称由客户端提供。举个例子,例如要向远程某个地址请求股票信息,请求的url可能为http://www.example.com/getStock?ticker=1234&callback=processStock,这个服务器端可能就会返回processStock({"price": 1234}),processStock是我们在请求传递的callback参数,它是在本地定义的一个JavaScript函数,括号内部是个json对象,它表示请求股票的价格。关于jsonp更详尽的解释可以参见这篇文章,我们这里需要明白的是jsonp的请求并不是通过XmlHttpRequest发送的,而是通过动态创建script元素来发送请求,script的请求也是这样做的。在jQuery中发送jsonp请求只需要在url参数中包含"=?"就可以了,例如对于上面的例子,使用jQuery调用就是这样的:
第3402行的正则表达式进行的正是这样的判断。第3406-3407行对s.data按URL编码进行编码,{name1: "value1", name2: "value2"}会编码成"name1=value1&name2=value2"。第3401-3417行当请求数据类型为jsonp,但url不包含"=?"时,给s.url或s.data添加s.jsonp=?参数,当s.jsonp没有指定时为callback。第3420-3433行仍然处理的是jsonp数据请求,第3421-3426行将请求url中的?替换成在jQuery定义的一个函数名称,它由"jsonp"再加下一个递增的数字组成。第3433-3442行定义了这个函数,这个函数会在远程脚本加载完成时被调用,在这个函数内部调用了success()和complete(),而它们又会调用参数s中传递的函数函数。注意3430将请求数据类型改成了script,这是因为jsonp请求和script请求采用相同的方式发送请求。
3445 if ( s.dataType == "script" && s.cache == null )
3446 s.cache = false;
3447
3448 if ( s.cache === false && type == "GET" ) {
3449 var ts = now();
3450 // try replacing _= if it is there
3451 var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
3452 // if nothing was replaced, add timestamp to the end
3453 s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
3454 }
3455
3456 // If data is available, append data to url for get requests
3457 if ( s.data && type == "GET" ) {
3458 s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
3459
3460 // IE likes to send both get and post data, prevent this
3461 s.data = null;
3462 }
3463
3464 // Watch for a new set of requests
3465 if ( s.global && ! jQuery.active++ )
3466 jQuery.event.trigger( "ajaxStart" );
上面的代码主要是构造GET请求的URL,缓存禁用时要给url最后加上一个时间参数(第3448-3454行),这样可以避免浏览器对请求的缓存,另外data中的参数也会加到url参数中去(第3457-3461行)。第3465-3466行触发全局事件ajaxStart,注意ajaxStart并不是每次ajax请求都会触发,它只会在第一次调用ajax方法触发。
3468 // Matches an absolute URL, and saves the domain
3469 var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
3470
3471 // If we're requesting a remote document
3472 // and trying to load JSON or Script with a GET
3473 if ( s.dataType == "script" && type == "GET" && parts
3474 && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
3475
3476 var head = document.getElementsByTagName("head")[0];
3477 var script = document.createElement("script");
3478 script.src = s.url;
3479 if (s.scriptCharset)
3480 script.charset = s.scriptCharset;
3481
3482 // Handle Script loading
3483 if ( !jsonp ) {
3484 var done = false;
3485
3486 // Attach handlers for all browsers
3487 script.onload = script.onreadystatechange = function(){
3488 if ( !done && (!this.readyState ||
3489 this.readyState == "loaded" || this.readyState == "complete") ) {
3490 done = true;
3491 success();
3492 complete();
3493
3494 // Handle memory leak in IE
3495 script.onload = script.onreadystatechange = null;
3496 head.removeChild( script );
3497 }
3498 };
3499 }
3500
3501 head.appendChild(script);
3502
3503 // We handle everything using the script element injection
3504 return undefined;
3505 }
上面的代码是处理script格式的请求,别忘了,jsonp格式的请求在前面已经将格式设置为script了,因为jsonp格式的请求也是在这里处理的。第3469行的正则表达式取出url的协议和主机名。虽然这段代码比较长,但实现应该是很直观的,第3476-3480行创建script元素,并将它的src设置为s.url,第3501行将它添加到head元素中。对于script格式(非jsonp)的请求设置回调函数,在该回调函数中调用success()和complete()函数,它们调用用户定义的回调函数(第3483-3499行)。第3504,返回undefined,对jsonp和script格式的请求结束,由于它们没有使用XmlHttpRequest,因此返回undefined。
3507 var requestDone = false;
3508
3509 // Create the request object
3510 var xhr = s.xhr();
3511
3512 // Open the socket
3513 // Passing null username, generates a login popup on Opera (#2865)
3514 if( s.username )
3515 xhr.open(type, s.url, s.async, s.username, s.password);
3516 else
3517 xhr.open(type, s.url, s.async);
3518
3519 // Need an extra try/catch for cross domain requests in Firefox 3
3520 try {
3521 // Set the correct header, if data is being sent
3522 if ( s.data )
3523 xhr.setRequestHeader("Content-Type", s.contentType);
3524
3525 // Set the If-Modified-Since header, if ifModified mode.
3526 if ( s.ifModified )
3527 xhr.setRequestHeader("If-Modified-Since",
3528 jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
3529
3530 // Set header so the called script knows that it's an XMLHttpRequest
3531 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
3532
3533 // Set the Accepts header for the server, depending on the dataType
3534 xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
3535 s.accepts[ s.dataType ] + ", */*" :
3536 s.accepts._default );
3537 } catch(e){}
3538
3539 // Allow custom headers/mimetypes and early abort
3540 if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
3541 // Handle the global AJAX counter
3542 if ( s.global && ! --jQuery.active )
3543 jQuery.event.trigger( "ajaxStop" );
3544 // close opended socket
3545 xhr.abort();
3546 return false;
3547 }
3548
3549 if ( s.global )
3550 jQuery.event.trigger("ajaxSend", [xhr, s]);
上面的代码创建XmlHttpRequest(第3510行),打开连接(第3514-3517行),设置请求参数(第3520-3537行),触发全局事件(第3540-3550行),所有的全局事件仅在s.globa执行为true(这是默认值)时才会触发。用户可以定义beforeSend回调函数,如果它返回false,则不会发送请求(第3540-3547行)。
3552 // Wait for a response to come back
3553 var onreadystatechange = function(isTimeout){
3554 // The request was aborted, clear the interval and decrement jQuery.active
3555 if (xhr.readyState == 0) {
3556 if (ival) {
3557 // clear poll interval
3558 clearInterval(ival);
3559 ival = null;
3560 // Handle the global AJAX counter
3561 if ( s.global && ! --jQuery.active )
3562 jQuery.event.trigger( "ajaxStop" );
3563 }
3564 // The transfer is complete and the data is available, or the request timed out
3565 } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
3566 requestDone = true;
3567
3568 // clear poll interval
3569 if (ival) {
3570 clearInterval(ival);
3571 ival = null;
3572 }
3573
3574 status = isTimeout == "timeout" ? "timeout" :
3575 !jQuery.httpSuccess( xhr ) ? "error" :
3576 s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
3577 "success";
3578
3579 if ( status == "success" ) {
3580 // Watch for, and catch, XML document parse errors
3581 try {
3582 // process the data (runs the xml through httpData regardless of callback)
3583 data = jQuery.httpData( xhr, s.dataType, s );
3584 } catch(e) {
3585 status = "parsererror";
3586 }
3587 }
3588
3589 // Make sure that the request was successful or notmodified
3590 if ( status == "success" ) {
3591 // Cache Last-Modified header, if ifModified mode.
3592 var modRes;
3593 try {
3594 modRes = xhr.getResponseHeader("Last-Modified");
3595 } catch(e) {} // swallow exception thrown by FF if header is not available
3596
3597 if ( s.ifModified && modRes )
3598 jQuery.lastModified[s.url] = modRes;
3599
3600 // JSONP handles its own success callback
3601 if ( !jsonp )
3602 success();
3603 } else
3604 jQuery.handleError(s, xhr, status);
3605
3606 // Fire the complete handlers
3607 complete();
3608
3609 if ( isTimeout )
3610 xhr.abort();
3611
3612 // Stop memory leaks
3613 if ( s.async )
3614 xhr = null;
3615 }
3616 };
3618 if ( s.async ) {
3619 // don't attach the handler to the request, just poll it instead
3620 var ival = setInterval(onreadystatechange, 13);
3621
3622 // Timeout checker
3623 if ( s.timeout > 0 )
3624 setTimeout(function(){
3625 // Check to see if the request is still happening
3626 if ( xhr && !requestDone )
3627 onreadystatechange( "timeout" );
3628 }, s.timeout);
3629 }
定义onreadystate函数,但是要注意的是它并没有设置成xhr的onreadystatechange事件,相反,它使用setInterval每隔13毫秒轮询一次(第3619行),这是与我们通常使用的ajax实现不一样,我想jQuery这样做,大概是为了灵活性,这使得很容易处理请求超时(第3623-3628行),但超时后,会调用onreadystatechange( "timeout"),因此在onreadystatechange若检测到参数值为'timeout'时就表示请求已经超时,需要取消。
第3555-3563行处理请求放弃的情况,第3565-3615行处理请求完成或请求超时的情况。第3569-3572清除轮询,第3574-3577判断请求的状态,或者timeout或者error或者notmodified或者success。第3579-3604行的处理应该是很直观,有错就处理错误,成功就调用成功的函数。不管成功与否,最后都要调用complete()函数(第3607行),如果请求超时了还要放弃请求(第3609-1610行)。
3632 try {
3633 xhr.send(s.data);
3634 } catch(e) {
3635 jQuery.handleError(s, xhr, null, e);
3636 }
3637
3638 // firefox 1.5 doesn't fire statechange for sync requests
3639 if ( !s.async )
3640 onreadystatechange();
3641
3642 function success(){
3643 // If a local callback was specified, fire it and pass it the data
3644 if ( s.success )
3645 s.success( data, status );
3646
3647 // Fire the global callback
3648 if ( s.global )
3649 jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
3650 }
3651
3652 function complete(){
3653 // Process result
3654 if ( s.complete )
3655 s.complete(xhr, status);
3656
3657 // The request was completed
3658 if ( s.global )
3659 jQuery.event.trigger( "ajaxComplete", [xhr, s] );
3660
3661 // Handle the global AJAX counter
3662 if ( s.global && ! --jQuery.active )
3663 jQuery.event.trigger( "ajaxStop" );
3664 }
3665
3666 // return XMLHttpRequest to allow aborting the request etc.
3667 return xhr;
3668 },
t第3632-3636行发送请求,同步调用时手动触发onreadystatechange(第3639-3640行),3642-3650定义了成功时的处理,调用用户设置的成功回调函数,并触发全局事件。第3652-3664行定义了完成时处理,调用用户设置的完成回调函数,并触发全局事件。最后返回XmlHttpRequest实例xhr(第3667行)。
以上就是ajax函数的全部实现,也是jQuery的ajax实现之精华所在,其它的ajax函数都要调用这个函数,就不再讲了。
jQuery中的所有Ajax调用都是通过jQuery.ajax函数来完成的,get, post, getJSON, getScript都只是简单地将调用转发给ajax函数。因此我们就从ajax函数入手。在潜入复杂的实现之前,先考虑一下自己实现ajax会怎样做是非常有益的。显然,我们做首先创建XmlHttpRequest,然后发送异步请求,在onreadystate回调中根据请求的结果再调用自定义的回调函数(如onsuccess, oncomplte, onerror)。jQuery的实现当然要比比复杂得多,因为它不仅要处理异步请求,也要处理同步请求,对其不同格式的处理过程也不一样,但经过这番思考之后,至少心里有个底,然后在复杂的jQuery实现里不致迷失方向。先来看ajax函数的前部分:
3397 ajax: function( s ) {
3398 // Extend the settings, but re-extend 's' so that it can be
3399 // checked again later (in the test suite, specifically)
3400 s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
3401
3402 var jsonp, jsre = /=\?(&|$)/g, status, data,
3403 type = s.type.toUpperCase();
3404
3405 // convert data if not already a string
3406 if ( s.data && s.processData && typeof s.data !== "string" )
3407 s.data = jQuery.param(s.data);
3408
3409 // Handle JSONP Parameter Callbacks
3410 if ( s.dataType == "jsonp" ) {
3411 if ( type == "GET" ) {
3412 if ( !s.url.match(jsre) )
3413 s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
3414 } else if ( !s.data || !s.data.match(jsre) )
3415 s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
3416 s.dataType = "json";
3417 }
3418
3419 // Build temporary JSONP function
3420 if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
3421 jsonp = "jsonp" + jsc++;
3422
3423 // Replace the =? sequence both in the query string and the data
3424 if ( s.data )
3425 s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
3426 s.url = s.url.replace(jsre, "=" + jsonp + "$1");
3427
3428 // We need to make sure
3429 // that a JSONP style response is executed properly
3430 s.dataType = "script";
3431
3432 // Handle JSONP-style loading
3433 window[ jsonp ] = function(tmp){
3434 data = tmp;
3435 success();
3436 complete();
3437 // Garbage collect
3438 window[ jsonp ] = undefined;
3439 try{ delete window[ jsonp ]; } catch(e){}
3440 if ( head )
3441 head.removeChild( script );
3442 };
3443 }
可以看到ajax函数只接受一个参数s,它包含所有请求所需的信息,如请求方法(s.type),请求的url(s.url),请求的数据(s.data),请求返回的数据类型(s.dataType),回调函数(s.success, s.error, s.complete)等,该方法返回XmlHttpRequest对象。前面的部分主要用于处理jsonp协议,我们知道由于Ajax请求由于安全限制,不能发送跨域请求,jsonp可以解除这个限制,它并不使用XmlHttpRequest去发送请求,而是使用动态创建script元素的技术,并将它的src的元素设置成请求的url,这就要求远程返回的是JavaScript代码,而不能仅仅是数据。jsonp是这样处理的,服务器端返回的数据仍然使用json协议,但它会将数据放在一个函数调用中,函数名称由客户端提供。举个例子,例如要向远程某个地址请求股票信息,请求的url可能为http://www.example.com/getStock?ticker=1234&callback=processStock,这个服务器端可能就会返回processStock({"price": 1234}),processStock是我们在请求传递的callback参数,它是在本地定义的一个JavaScript函数,括号内部是个json对象,它表示请求股票的价格。关于jsonp更详尽的解释可以参见这篇文章,我们这里需要明白的是jsonp的请求并不是通过XmlHttpRequest发送的,而是通过动态创建script元素来发送请求,script的请求也是这样做的。在jQuery中发送jsonp请求只需要在url参数中包含"=?"就可以了,例如对于上面的例子,使用jQuery调用就是这样的:
$.ajax({url: "http://www.example.com/getStock?ticker=1234&callback=?", ....})
第3402行的正则表达式进行的正是这样的判断。第3406-3407行对s.data按URL编码进行编码,{name1: "value1", name2: "value2"}会编码成"name1=value1&name2=value2"。第3401-3417行当请求数据类型为jsonp,但url不包含"=?"时,给s.url或s.data添加s.jsonp=?参数,当s.jsonp没有指定时为callback。第3420-3433行仍然处理的是jsonp数据请求,第3421-3426行将请求url中的?替换成在jQuery定义的一个函数名称,它由"jsonp"再加下一个递增的数字组成。第3433-3442行定义了这个函数,这个函数会在远程脚本加载完成时被调用,在这个函数内部调用了success()和complete(),而它们又会调用参数s中传递的函数函数。注意3430将请求数据类型改成了script,这是因为jsonp请求和script请求采用相同的方式发送请求。
3445 if ( s.dataType == "script" && s.cache == null )
3446 s.cache = false;
3447
3448 if ( s.cache === false && type == "GET" ) {
3449 var ts = now();
3450 // try replacing _= if it is there
3451 var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
3452 // if nothing was replaced, add timestamp to the end
3453 s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
3454 }
3455
3456 // If data is available, append data to url for get requests
3457 if ( s.data && type == "GET" ) {
3458 s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
3459
3460 // IE likes to send both get and post data, prevent this
3461 s.data = null;
3462 }
3463
3464 // Watch for a new set of requests
3465 if ( s.global && ! jQuery.active++ )
3466 jQuery.event.trigger( "ajaxStart" );
上面的代码主要是构造GET请求的URL,缓存禁用时要给url最后加上一个时间参数(第3448-3454行),这样可以避免浏览器对请求的缓存,另外data中的参数也会加到url参数中去(第3457-3461行)。第3465-3466行触发全局事件ajaxStart,注意ajaxStart并不是每次ajax请求都会触发,它只会在第一次调用ajax方法触发。
3468 // Matches an absolute URL, and saves the domain
3469 var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
3470
3471 // If we're requesting a remote document
3472 // and trying to load JSON or Script with a GET
3473 if ( s.dataType == "script" && type == "GET" && parts
3474 && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
3475
3476 var head = document.getElementsByTagName("head")[0];
3477 var script = document.createElement("script");
3478 script.src = s.url;
3479 if (s.scriptCharset)
3480 script.charset = s.scriptCharset;
3481
3482 // Handle Script loading
3483 if ( !jsonp ) {
3484 var done = false;
3485
3486 // Attach handlers for all browsers
3487 script.onload = script.onreadystatechange = function(){
3488 if ( !done && (!this.readyState ||
3489 this.readyState == "loaded" || this.readyState == "complete") ) {
3490 done = true;
3491 success();
3492 complete();
3493
3494 // Handle memory leak in IE
3495 script.onload = script.onreadystatechange = null;
3496 head.removeChild( script );
3497 }
3498 };
3499 }
3500
3501 head.appendChild(script);
3502
3503 // We handle everything using the script element injection
3504 return undefined;
3505 }
上面的代码是处理script格式的请求,别忘了,jsonp格式的请求在前面已经将格式设置为script了,因为jsonp格式的请求也是在这里处理的。第3469行的正则表达式取出url的协议和主机名。虽然这段代码比较长,但实现应该是很直观的,第3476-3480行创建script元素,并将它的src设置为s.url,第3501行将它添加到head元素中。对于script格式(非jsonp)的请求设置回调函数,在该回调函数中调用success()和complete()函数,它们调用用户定义的回调函数(第3483-3499行)。第3504,返回undefined,对jsonp和script格式的请求结束,由于它们没有使用XmlHttpRequest,因此返回undefined。
3507 var requestDone = false;
3508
3509 // Create the request object
3510 var xhr = s.xhr();
3511
3512 // Open the socket
3513 // Passing null username, generates a login popup on Opera (#2865)
3514 if( s.username )
3515 xhr.open(type, s.url, s.async, s.username, s.password);
3516 else
3517 xhr.open(type, s.url, s.async);
3518
3519 // Need an extra try/catch for cross domain requests in Firefox 3
3520 try {
3521 // Set the correct header, if data is being sent
3522 if ( s.data )
3523 xhr.setRequestHeader("Content-Type", s.contentType);
3524
3525 // Set the If-Modified-Since header, if ifModified mode.
3526 if ( s.ifModified )
3527 xhr.setRequestHeader("If-Modified-Since",
3528 jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
3529
3530 // Set header so the called script knows that it's an XMLHttpRequest
3531 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
3532
3533 // Set the Accepts header for the server, depending on the dataType
3534 xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
3535 s.accepts[ s.dataType ] + ", */*" :
3536 s.accepts._default );
3537 } catch(e){}
3538
3539 // Allow custom headers/mimetypes and early abort
3540 if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
3541 // Handle the global AJAX counter
3542 if ( s.global && ! --jQuery.active )
3543 jQuery.event.trigger( "ajaxStop" );
3544 // close opended socket
3545 xhr.abort();
3546 return false;
3547 }
3548
3549 if ( s.global )
3550 jQuery.event.trigger("ajaxSend", [xhr, s]);
上面的代码创建XmlHttpRequest(第3510行),打开连接(第3514-3517行),设置请求参数(第3520-3537行),触发全局事件(第3540-3550行),所有的全局事件仅在s.globa执行为true(这是默认值)时才会触发。用户可以定义beforeSend回调函数,如果它返回false,则不会发送请求(第3540-3547行)。
3552 // Wait for a response to come back
3553 var onreadystatechange = function(isTimeout){
3554 // The request was aborted, clear the interval and decrement jQuery.active
3555 if (xhr.readyState == 0) {
3556 if (ival) {
3557 // clear poll interval
3558 clearInterval(ival);
3559 ival = null;
3560 // Handle the global AJAX counter
3561 if ( s.global && ! --jQuery.active )
3562 jQuery.event.trigger( "ajaxStop" );
3563 }
3564 // The transfer is complete and the data is available, or the request timed out
3565 } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
3566 requestDone = true;
3567
3568 // clear poll interval
3569 if (ival) {
3570 clearInterval(ival);
3571 ival = null;
3572 }
3573
3574 status = isTimeout == "timeout" ? "timeout" :
3575 !jQuery.httpSuccess( xhr ) ? "error" :
3576 s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
3577 "success";
3578
3579 if ( status == "success" ) {
3580 // Watch for, and catch, XML document parse errors
3581 try {
3582 // process the data (runs the xml through httpData regardless of callback)
3583 data = jQuery.httpData( xhr, s.dataType, s );
3584 } catch(e) {
3585 status = "parsererror";
3586 }
3587 }
3588
3589 // Make sure that the request was successful or notmodified
3590 if ( status == "success" ) {
3591 // Cache Last-Modified header, if ifModified mode.
3592 var modRes;
3593 try {
3594 modRes = xhr.getResponseHeader("Last-Modified");
3595 } catch(e) {} // swallow exception thrown by FF if header is not available
3596
3597 if ( s.ifModified && modRes )
3598 jQuery.lastModified[s.url] = modRes;
3599
3600 // JSONP handles its own success callback
3601 if ( !jsonp )
3602 success();
3603 } else
3604 jQuery.handleError(s, xhr, status);
3605
3606 // Fire the complete handlers
3607 complete();
3608
3609 if ( isTimeout )
3610 xhr.abort();
3611
3612 // Stop memory leaks
3613 if ( s.async )
3614 xhr = null;
3615 }
3616 };
3618 if ( s.async ) {
3619 // don't attach the handler to the request, just poll it instead
3620 var ival = setInterval(onreadystatechange, 13);
3621
3622 // Timeout checker
3623 if ( s.timeout > 0 )
3624 setTimeout(function(){
3625 // Check to see if the request is still happening
3626 if ( xhr && !requestDone )
3627 onreadystatechange( "timeout" );
3628 }, s.timeout);
3629 }
定义onreadystate函数,但是要注意的是它并没有设置成xhr的onreadystatechange事件,相反,它使用setInterval每隔13毫秒轮询一次(第3619行),这是与我们通常使用的ajax实现不一样,我想jQuery这样做,大概是为了灵活性,这使得很容易处理请求超时(第3623-3628行),但超时后,会调用onreadystatechange( "timeout"),因此在onreadystatechange若检测到参数值为'timeout'时就表示请求已经超时,需要取消。
第3555-3563行处理请求放弃的情况,第3565-3615行处理请求完成或请求超时的情况。第3569-3572清除轮询,第3574-3577判断请求的状态,或者timeout或者error或者notmodified或者success。第3579-3604行的处理应该是很直观,有错就处理错误,成功就调用成功的函数。不管成功与否,最后都要调用complete()函数(第3607行),如果请求超时了还要放弃请求(第3609-1610行)。
3632 try {
3633 xhr.send(s.data);
3634 } catch(e) {
3635 jQuery.handleError(s, xhr, null, e);
3636 }
3637
3638 // firefox 1.5 doesn't fire statechange for sync requests
3639 if ( !s.async )
3640 onreadystatechange();
3641
3642 function success(){
3643 // If a local callback was specified, fire it and pass it the data
3644 if ( s.success )
3645 s.success( data, status );
3646
3647 // Fire the global callback
3648 if ( s.global )
3649 jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
3650 }
3651
3652 function complete(){
3653 // Process result
3654 if ( s.complete )
3655 s.complete(xhr, status);
3656
3657 // The request was completed
3658 if ( s.global )
3659 jQuery.event.trigger( "ajaxComplete", [xhr, s] );
3660
3661 // Handle the global AJAX counter
3662 if ( s.global && ! --jQuery.active )
3663 jQuery.event.trigger( "ajaxStop" );
3664 }
3665
3666 // return XMLHttpRequest to allow aborting the request etc.
3667 return xhr;
3668 },
t第3632-3636行发送请求,同步调用时手动触发onreadystatechange(第3639-3640行),3642-3650定义了成功时的处理,调用用户设置的成功回调函数,并触发全局事件。第3652-3664行定义了完成时处理,调用用户设置的完成回调函数,并触发全局事件。最后返回XmlHttpRequest实例xhr(第3667行)。
以上就是ajax函数的全部实现,也是jQuery的ajax实现之精华所在,其它的ajax函数都要调用这个函数,就不再讲了。
0 评论:
发表评论