jQuery源码阅读(十四)---aJax 模块与异步队列联系
来源:互联网 发布:冰箱 知乎 编辑:程序博客网 时间:2024/06/06 01:42
关于ajax
,jQuery
做了封装,并且考虑了很多浏览器兼容问题,以及跨域问题。当然,这种异步操作离不开我们之前分析的延迟对象。所以这一次,主要是对ajax
模块中对于延迟对象的应用进行分析。
$.ajax函数
先来分析$.ajax()
函数。
在这里,我必须贴一张图来展示下ajax
源码的整体框架。此图引用于高云(jQuery
技术内幕第13章)
上图清楚得描述了整个ajax
过程中的一系列流程,同时是按照源码顺序列出来的。根据上图,我们也能大体还原出来一个ajax
源码框架。
通常,我们调ajax
时,比较常见的就是 $.ajax({ url: 'XX.php', success: function(){ } })
这种形式, 但也有$.ajax('XX.php').done(function(){ }).fail(function(){ })
这种形式。所以,不难想象,$.ajax()返回的也是一个延迟对象。即上图上面最后返回的jqXHR
对象。而这个jqXHR
对象又类似于我们原生XMLHttpRequest
对象,需要有自己的一些方法和属性,因此,可以利用延迟对象的promise(arguments)
方法来扩展延迟对象。
function( url, options){ //创建延迟对象 var deferred = $.Deferred(); //创建一个回调对象,用于最终完成时,触发完成回调函数。 var completeDeferred = $.Callbacks('once memory') jqXHR = { //拥有一些XMLHttpRequest的属性和方法,比如readyState、 setRequestHeader等等。 readyState: 0, setRequestHeader: function(){ } } deferred.promise(jqXHR); //此时jqXHR是一个延迟对象,并且拥有自己特有的属性和方法 //将我们常用的success、error、complete这些回调方法分别与延迟对象的回调列表对应起来。 jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; jqXHR.complete = completeDeferred.add; return jqXHR;}
上面这部分框架,只是创建jqXHR对象,并将其与延迟对象的各个列表联系起来,并最终返回这个延迟对象的过程。那么,问题来了,到底随着请求的产生,发送及响应,是如何触发回调函数的?
我们再回过头看上面的图,最后当transport.send( requestHeaders, done );
执行之后,这个函数主要执行了xhr.send();
这个函数,然后监听readyStateChange事件,由xhr.responseText
或者xhr.responseXML
判断,有响应数据时,便会触发send
中的回调函数done
。
因为transport.send
针对了script
和 其他数据类型两种情况,我们这里只讨论后一种情况。其transport.send
中的部分源码如下:
//创建XMLHttpRequest对象//打开socket连接:xhr.open()if ( s.username ) { xhr.open( s.type, s.url, s.async, s.username, s.password );} else { xhr.open( s.type, s.url, s.async );}//设置HTTP请求头信息//接着是发送请求:xhr.send(),如果是post方法,有参数xhr.send( ( s.hasContent && s.data ) || null );//下面的思想,跟我们用原生XMLHttpRequest对象实现aJax是一样的。也是通过监听readyState是否变化的事件从而触发回调函数//如果是同步ajax请求或者状态已经完成,那么手动执行回调函数if ( !s.async || xhr.readyState === 4 ) { callback();} else { //如果是异步,并且状态还未变成已完成,那么通过监听readystatechange事件来触发回调函数 handle = ++xhrId; if ( xhrOnUnloadAbort ) { // 这里主要是在IE浏览器中 if ( !xhrCallbacks ) { xhrCallbacks = {}; //因为在IE浏览器中,关闭浏览器时,仍然保持着连接,所以需要手动将所有的ajax请求回调函数取消。 jQuery( window ).unload( xhrOnUnloadAbort ); } xhrCallbacks[ handle ] = callback; } //不是IE浏览器时,监听readystatechange事件 xhr.onreadystatechange = callback;}
那么回调函数callback 是干嘛的?
//isAbort表示是否取消请求function( _, isAbort ) { var status, statusText, responseHeaders, responses, xml; // Firefox throws exceptions when accessing properties // of an xhr when a network error occured // http://helpful.knobsdials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) try { // Was never called and is aborted or complete if ( callback && ( isAbort || xhr.readyState === 4 ) ) { // Only called once callback = undefined; // Do not keep as active anymore if ( handle ) { xhr.onreadystatechange = jQuery.noop; if ( xhrOnUnloadAbort ) { delete xhrCallbacks[ handle ]; } } // If it's an abort if ( isAbort ) { // Abort it manually if needed if ( xhr.readyState !== 4 ) { xhr.abort(); } } else { status = xhr.status; responseHeaders = xhr.getAllResponseHeaders(); responses = {}; xml = xhr.responseXML; // Construct response list if ( xml && xml.documentElement /* #4958 */ ) { responses.xml = xml; } // When requesting binary data, IE6-9 will throw an exception // on any attempt to access responseText (#11426) try { responses.text = xhr.responseText; } catch( _ ) { } // Firefox throws an exception when accessing // statusText for faulty cross-domain requests try { statusText = xhr.statusText; } catch( e ) { // We normalize with Webkit giving an empty statusText statusText = ""; } // Filter status for non standard behaviors // If the request is local and we have data: assume a success // (success with no data won't get notified, that's the best we // can do given current implementations) if ( !status && s.isLocal && !s.crossDomain ) { status = responses.text ? 200 : 404; // IE - #1450: sometimes returns 1223 when it should be 204 } else if ( status === 1223 ) { status = 204; } } } } catch( firefoxAccessException ) { if ( !isAbort ) { complete( -1, firefoxAccessException ); } } //如果得到响应数据,触发complete函数,这个函数是外部传过来的函数,也就是我们外部的done回调函数 if ( responses ) { complete( status, statusText, responses, responseHeaders ); }}
而done
函数中,主要根据status
来判断响应是否成功,如果成功,触发jqXHR的成功回调列表中的回调;如果失败,则触发jqXHR的失败回调列表中的回调,除了这些处理,还会触发complete
回调函数,同时会根据情况触发各种ajax
全局函数。
function done( status, nativeStatusText, responses, headers ) { //如果state为2,说明这个回调已经执行过了,不会再执行第二次了 if ( state === 2 ) { return; } 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; var isSuccess, success, error, statusText = nativeStatusText, response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, lastModified, etag; // 下面这部分代码主要是根据status状态,来判断是否请求成功。 //请求成功时,isSuccess标志就被设为true if ( status >= 200 && status < 300 || status === 304 ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { jQuery.lastModified[ ifModifiedKey ] = lastModified; } if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { jQuery.etag[ ifModifiedKey ] = etag; } } // If not modified if ( status === 304 ) { statusText = "notmodified"; isSuccess = true; //If we have data } else { try { success = ajaxConvert( s, response ); statusText = "success"; isSuccess = true; } catch(e) { // We have a parsererror statusText = "parsererror"; error = e; } } } else { // We extract error from statusText // then normalize statusText and status for non-aborts error = statusText; if ( !statusText || status ) { statusText = "error"; if ( status < 0 ) { status = 0; } } } // Set data for the fake xhr object jqXHR.status = status; jqXHR.statusText = "" + ( nativeStatusText || statusText ); //由上面得到的请求成功与否的状态isSuccess //请求成功,触发deferred.resolveWith(),使该延迟对象处于resolve状态,执行成功回调列表里的回调函数 //请求失败,触发deferred.rejectWith(),使该延迟对象处于reject状态,执行失败回调列表里的回调函数 if ( isSuccess ) { deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); } else { deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); } //Status-dependent callbacks jqXHR.statusCode( statusCode ); statusCode = undefined; if ( fireGlobals ) { //触发ajax全局事件,ajaxSuccess或者 ajaxError事件 globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), [ jqXHR, s, isSuccess ? success : error ] ); } //执行completeDeferred回调对象的fireWith()方法,执行回调列表中的回调函数 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); // Handle the global AJAX counter if ( !( --jQuery.active ) ) { jQuery.event.trigger( "ajaxStop" ); } }}
上面done
函数主要是通过触发延迟对象的resolveWith
或rejectWith
方法来执行我们添加的success: function(){ }
或 error: function(){ }
方法,并且通过触发completeDeferred
回调对象的fireWith()
方法来执行我们添加的complete: function(){ }
方法,那么我们在参数中添加的方法是如何加到回调列表中去的?
jQuery中是这么做的:
for ( i in { success: 1, error: 1, complete: 1 } ) { jqXHR[ i ]( s[ i ] );}
通过对{ success: 1, error: 1, complete: 1 }
遍历,分别执行jqXHR.success
, jqXHR.error
, jqXHR.complete
方法。这三个方法在前面也讲过了,分别对应到deferred
对象和回调对象的done
, fail
和 add
方法。所以这个操作就是将s.success
, s.error
, s.complete
添加到对应回调列表中的。那么s.success
, s.error
, s.complete
分别是什么呢?
其中
//option是我们传入到ajax中的配置选项对象s = jQuery.ajaxSetup( {}, options )//s就是 我们传的配置选项与jQuery源码中默认的配置的组合,里面有我们传入的url,没传url的话默认就是自身的href(这是在jQuery源码默认的配置选项中设置的); 还有我们传入的success回调函数, error回调函数以及complete函数。因此,上面说的s.success, s.error和s.complete其实就是我们传入的回调函数。
这样在done
函数中,触发resolveWith
或者rejectWith
,就会执行对应列表中的回调了,也就是我们传的回调函数。complete
也是同样的道理。
这样,就实现了ajax在请求的过程中,根据响应的成功与否,触发不同的回调列表中的函数,以此来实现异步请求的目的。这个过程很复杂,且比较绕,并且期间处理了很多很多情况,比如设置头部,处理跨域,这些都还没深入,只是从异步队列角度来分析,如果就实现了异步,旨在对异步队列应用有更深的理解。
- jQuery源码阅读(十四)---aJax 模块与异步队列联系
- jQuery源码阅读(十三)---jQuery异步队列模块
- 并发(十四):异步Servlet与AJAX的区别与联系
- jQuery源码分析-05异步队列 Deferred
- spark源码阅读(十四)---sparkEnv类
- JavaScript、JQuery、Ajax区别与联系
- Nginx源码阅读(模块)
- jQuery.ajax的同步与异步
- jquery ajax的 异步与同步
- jquery ajax同步与异步的使用
- jquery ajax请求同步与异步问题
- JQuery的AJAX的同步与异步
- ajax与jquery批量更新,异步请求
- JQuery与Ajax实现异步局部刷新
- jquery中同步异步的区别与联系
- Strust2第(十四)篇《struts2+ajax实现异步验证》
- 细谈struts2(十四)struts2+ajax实现异步验证
- pytorch学习笔记(十四): DataLoader源码阅读
- linux临时或永久修改DNS
- xftp从windows传输文件到linux,拒绝下载
- 查询Mysql数据库的所有存储过程和函数
- PAT 甲级 1012. The Best Rank (25)
- (100)TCP:socket、ServerSocket
- jQuery源码阅读(十四)---aJax 模块与异步队列联系
- 滚动事件判断滚动条
- windows7修改双系统启动项名称、先后顺序、等待时间
- centos创建二级域名
- python命令行解析模块argparse
- Android开源项目及库整理总结
- 洛谷OJ上的A+B花(zhuang)式(bi)解法
- 递归—— 一只小蜜蜂
- eclipse去除validate