jQuery deferred应用之ajax详细源码分析(二)
2011-12-04 20:03
781 查看
在上一节中,我只贴出了$.Deferred的源码分析,并没用讲解怎么使用它,现在我们先看看$.ajax是如何使用它,让我们进行异步任务的处理。
如果没看上节的代码,请先稍微了解一下 jQuery Deferred的工作原理,这样ajax你才能用得更好。
这里我将以一个跨域请求 "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"这个js文件为例,讲解jQuery的ajax工作原理,知道了原理,什么ajax都是浮云了。
先看这张图片,对$.ajax先来看全局的展望
这里外部有两个变量 prefilters = {}, transports = {}, 他们将会应为addToPrefiltersOrTransports() 通过闭包变为
prefilters={json:[fun],jsonp:[fun],script:[fun]} transports={*:[fun],script:[fun]} 这里先不讨论这两个变量,我们这次用实例开始吧
这里,设置跨域请求js文件,简单的参数配置,前面说过jQuery ajax有强大的ajaxSettings参数配置,这里为什么$.ajax().done().success()呢?其实这个$.ajax()会返回一个jqXHR,然后这个对象
因为这句代码deferred.promise(jqXHR),使得jqXHR具有deferred对象的所有只读方法。详情见上章,接下来一步步看这个代码如何执行.
上面代码的-->指出了这段代码关键的运行,以及为什么我们可以直接$.ajax().done().done()异步处理任务,而且可以同时增加多个回调函数,这多得多亏$.Deferred().
好吧,重点来分析怎么transport.send(requestHeaders,done)来跨域请求我们的js文件吧...怎么jQuery一个ajax这么复杂?还不是为了迎合上千万使用者不同的使用目的。也许对你来说,
你只需要一个简单的ajax请求,我才不管他跨域了没有!这里就是jQuery的一个缺点吧,木办法啊,你们怎么多使用者,我要每个都照顾啊!好吧,来分析js怎么请求的吧
这只是一个简单的跨域请求js文件,怎么这么多代码?哎,这个jQuery ajax的参数配置太多了,他会不停的if再if ,这些if造就了jQuery的调用接口这么灵活了,可以这么写,也可以那么写,满足不同
的人的口味。 分析到这里,可能你已经头很大了,木办法,它确实很难,这只是ajax的一部分。
好吧,我承认上面都是低级$.ajax方法,为了满足调用者使用更简单,作者提供了$.get() $.post(),$.getScript(),$.getJSON(),哎,一切为了减少使用者的负担啊,本来是没必要加这些代码的
还是来看看,这几个方法怎么基于$.ajax写出来的,其实很简单了,如果你真真看懂了$.ajax ~_~
以上ajax代码分析了一半,就用了一个实例来分析,还有很多东西没讲,限于篇幅,下次吧,或者等我出书吧, 哎--> 我的梦想就是出书哎,然后教大家怎么更好的用jQuery。
顺便说下吧,我现在工资很低,没毕业,想挖我的公司联系我吧,哎,web前端js方向。
这也勉强算一个deferred应用吧,其实更多的是讲了ajax,以及跨域调用的问题.
下节应该是$(function(){}) $(funciton(){}) 为神马你的页面里面这么多这玩意,这玩意又是如何利用$.Deferred来完成的呢? 期待我有时间给大家讲吧 ^_^
如果没看上节的代码,请先稍微了解一下 jQuery Deferred的工作原理,这样ajax你才能用得更好。
这里我将以一个跨域请求 "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"这个js文件为例,讲解jQuery的ajax工作原理,知道了原理,什么ajax都是浮云了。
先看这张图片,对$.ajax先来看全局的展望
这里外部有两个变量 prefilters = {}, transports = {}, 他们将会应为addToPrefiltersOrTransports() 通过闭包变为
prefilters={json:[fun],jsonp:[fun],script:[fun]} transports={*:[fun],script:[fun]} 这里先不讨论这两个变量,我们这次用实例开始吧
var req = $.ajax("http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js", { dataType: "script", cache: true, timeout: "10000", statusCode: {200: function () { console.log("request finished and response is ready") }, 0: function () { console.log("request wrong")} } }).done(function () { console.log("跨域请求成功") }).success(function () { console.log("跨域请求成功2") });; req.done(function () { console.log("跨域请求成功3") });
这里,设置跨域请求js文件,简单的参数配置,前面说过jQuery ajax有强大的ajaxSettings参数配置,这里为什么$.ajax().done().success()呢?其实这个$.ajax()会返回一个jqXHR,然后这个对象
因为这句代码deferred.promise(jqXHR),使得jqXHR具有deferred对象的所有只读方法。详情见上章,接下来一步步看这个代码如何执行.
ajax: function (url, options) { //如果url是对象的话,冒充1.5版本之前的方法 -->$.ajax({url:"xx",data:{},}) if (typeof url === "object") { options = url; url = undefined; } //强制设置options成为一个对象 -->options为对象 options = options || {}; //这里创建了ajax的很多私有变量 var //创建最终的ajax参数对象s s = jQuery.ajaxSetup({}, options), -->通过$.ajaxSetup改变默认的参数配置,返回给s. // Callbacks context callbackContext = s.context || s, // Context for global events // It's the callbackContext if one was provided in the options //如果是Dom node或者jQuery collection globalEventContext = callbackContext !== s && (callbackContext.nodeType || callbackContext instanceof jQuery) ? jQuery(callbackContext) : jQuery.event, // 创建一个deferred对象 deferred = jQuery.Deferred(), -->使用到了Deferred对象 completeDeferred = jQuery._Deferred(), // Status-dependent callbacks //一组数值的HTTP代码和函数对象,当响应时调用了相应的代码 如$.ajax({statusCode: {404: function() {alert('page not found');}}); 默认为空对象 statusCode = s.statusCode || {}, -->这里我们传了一个 statusCode:{200:function(){}..} // ifModified key ifModifiedKey, // 请求头部 (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, // 响应 headers responseHeadersString, responseHeaders, // transport transport, //请求超时时间,异步请求下请求多少时间后中止请求, timeoutTimer, -->timeout:10000 // 判断是否是跨域请求的变量 parts, // jqXHR state state = 0, // To know if global events are to be dispatched fireGlobals, // Loop variable i, //伪劣的jqXHR, -->这里就是返回的jqXHR对象,初始一些key jqXHR = { readyState: 0, /*设置dataType,达到预期服务器返回的数据类型,,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断 */ setRequestHeader: function (name, value) { //这里状态还为0,表示还在发送请求前,设置请求头 if (!state) { var lname = name.toLowerCase(); name = requestHeadersNames[lname] = requestHeadersNames[lname] || name; requestHeaders[name] = value; //私有requestHeadersNames } return this; }, // Raw string getAllResponseHeaders: function () { return state === 2 ? responseHeadersString : null; }, // 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 === undefined ? null : match; }, // Overrides response content-type header overrideMimeType: function (type) { if (!state) { s.mimeType = type; } return this; }, // Cancel the request abort: function (statusText) { statusText = statusText || "abort"; if (transport) { transport.abort(statusText); } done(0, statusText); return this; } }; /* Callback for when everything is done, jQuery作者喜欢函数里嵌套函数,局部调用方便 * 2: request received * 4: request finished and response is ready <---readyState * 200: "OK" * 404: Page not found <----status */ function done(status, nativeStatusText, responses, headers) { -->这个done函数写到这个函数的内部,处理不同状态的req, 其实这个写法改成var done=function(){}更好 // 请求状态为2,表面已经请求过一次,立即返回 -->我是分析到下面回来的 ,第一次请求默认的state=0 if (state === 2) { return; } // State is "done" now state = 2; -->哦,处理了,为什么是2呢,因为XMLHttpRequest 状态为2的意思是 request received // 如果存在超时请求中止,清楚这个timeout if (timeoutTimer) { -->timeout有用了,这里被清除了~_~,我都响应请求了,肯定得把你删了 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; // If successful, handle type chaining if (status >= 200 && status < 300 || status === 304) { -->服务器成功处理了 我们的状态是200 // 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); --> 200状态,isSucess=true 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; } } } //设置jqXHR.status 其实也就是XMLHttpRequest状态 jqXHR.status = status; -->哦,这里设置了jqXHR的状态了 200 jqXHR.statusText = "" + (nativeStatusText || statusText); // 成功请求 resolveWith if (isSuccess) { deferred.resolveWith(callbackContext, [success, statusText, jqXHR]); ->成功了,当然得resolveWith,是不是发现done或者 success进来的函数一个个执行了^_^ } else { //失败请求 rejectWith ->resolve意味着什么?意味后面不管你done().done() 多少都会被直接触发 ~_~ deferred.rejectWith(callbackContext, [jqXHR, statusText, error]); } // 状态参数代码,默认为空 jqXHR.statusCode(statusCode); statusCode = undefined; if (fireGlobals) { globalEventContext.trigger("ajax" + (isSuccess ? "Success" : "Error"), [jqXHR, s, isSuccess ? success : error]); } // 不管请求成功与否都执行的complete resolveWith completeDeferred.resolveWith(callbackContext, [jqXHR, statusText]); -->原来$.ajax().complete在这里调用,所以不管成功没有都resolve了 if (fireGlobals) { globalEventContext.trigger("ajaxComplete", [jqXHR, s]); // Handle the global AJAX counter if (!(--jQuery.active)) { jQuery.event.trigger("ajaxStop"); } } } // 给这个jqXHR对象附上deferred对象的只读方法,包括done,always,isResolved等等,这里表明了,为什么$.ajax过后 //我们可以使用sucess,error,complete方法 deferred.promise(jqXHR); jqXHR.success = jqXHR.done; -->作者为了让我们更好的调用,又多了3个借口,哎,其实可以直接不需要的 jqXHR.error = jqXHR.fail; jqXHR.complete = completeDeferred.done; // Status-dependent callbacks 其实这个参数变量很少用,不过还可以,目前可以触发两个状态的statusCode: {200: function () //{ console.log("request ok") }, 404: function () { console.log("page not found")} } jqXHR.statusCode = function (map) { --> map={200:"..",0:".."} if (map) { var tmp; //临时函数变量 if (state < 2) { for (tmp in map) { statusCode[tmp] = [statusCode[tmp], map[tmp]]; } } else { tmp = map[jqXHR.status]; //jqXHR返回相应的callback,然后调用then方法触发 --> 我们的状态代码在这里调用,通过jqXHR.then方法调用 jqXHR.then(tmp, tmp); } } return this; }; // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) // We also use the url parameter if available // Remove 将url的hash去掉 rhash=/#.*$/ rprotocol = /^\/\// ajaxLocParts[1]="http:" s.url = ((url || s.url) + "").replace(rhash, "").replace(rprotocol, ajaxLocParts[1] + "//"); // 取出dataTypes list,不存在则为* rspacesAjax = /\s+/ 如:s.dataType=[*] s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().split(rspacesAjax); //默认设置crossDomain 同域请求为false, 跨域请求为true,如果想强制跨域请求(如JSONP形式)同一域,设置crossDomain为true if (s.crossDomain == null) { //同ajaxLocParts变量,这里也是通过这个请求的url来判断是否是跨域请求 parts = rurl.exec(s.url.toLowerCase()); //这里来判断是否是跨域请求,又是!! 主要判断请求的url parts与 ajaxLocParts -->这里智能判断你的请求地址是否是跨越,很 牛气吧,这里我们的corssDomain为true 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参数并且 processData=true&&s.data为一个对象吧 -->我们没传data参数,processData参数默认为true, 表示默认序列化参数data if (s.data && s.processData && typeof s.data !== "string") { //调用序列化函数 s.data = jQuery.param(s.data, s.traditional); } // Apply prefilters inspectPrefiltersOrTransports(prefilters, s, options, jqXHR); -->发送请求前,调用过滤器将 // If request was aborted inside a prefiler, stop there if (state === 2) { return false; } // We can fire global events as of now if asked to fireGlobals = s.global; // Uppercase the type s.type = s.type.toUpperCase(); // Determine if request has content //rnoContent= /^(?:GET|HEAD)$/ s.hasContent = !rnoContent.test(s.type); // Watch for a new set of requests if (fireGlobals && jQuery.active++ === 0) { jQuery.event.trigger("ajaxStart"); } // More options handling for requests with no content if (!s.hasContent) { //如果data参数存在,则append to url if (s.data) { -->我们以前是不是喜欢xx.php?data="xx"&&.. 这里会把序列化 后的data传到问号后面,很简单吧 //rquery = /\?/请求的url是否存在"?" 就比如请求"user.php?" -->这就是为什么data参数会被传到url?后面 s.url += (rquery.test(s.url) ? "&" : "?") + s.data; // #9682: remove data so that it's not used in an eventual retry delete s.data; } // Get ifModifiedKey before adding the anti-cache parameter ifModifiedKey = s.url; // Add anti-cache in url if needed if (s.cache === false) { var ts = jQuery.now(), // try replacing _= if it is there ret = s.url.replace(rts, "$1_=" + ts); // if nothing was replaced, add timestamp to the end s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : ""); } } // 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 If-Modified-Since and/or If-None-Match header, if in ifModified mode. if (s.ifModified) { ifModifiedKey = ifModifiedKey || s.url; if (jQuery.lastModified[ifModifiedKey]) { jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[ifModifiedKey]); } if (jQuery.etag[ifModifiedKey]) { jqXHR.setRequestHeader("If-None-Match", jQuery.etag[ifModifiedKey]); } } /*设置dataType,达到预期服务器返回的数据类型,如果没有dataType参数,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断 默认的s.accepts= accepts: {xml: "application/xml, text/xml", html: "text/html",text: "text/plain",json: "application/json, text/javascript","*": allTypes} allTypes= ['*\/']+['*'] 如果设置了dataType参数,即 s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().split(/\s+/); 比如:$(url,{dataType:"json"}) 这里第二个参数将成为 "application/json, text/javascript"+",*\/*;q=0.01" */ 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={} 一个额外的"{键:值}"对映射到请求一起发送 -->我们没设置头部 for (i in s.headers) { jqXHR.setRequestHeader(i, s.headers[i]); } // Allow custom headers/mimetypes and early abort if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2)) { // Abort if not done already jqXHR.abort(); return false; } //$.ajax().success(callback).error(callback).complete(callback) 在这里增加callback,这里表面现在的ajax请求可以不止一个回调函数 for (i in { success: 1, error: 1, complete: 1 }) { jqXHR[i](s[i]); } // Get transport 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设置请求超时时间(毫秒),超时请求后,jqXHR.abort() 中止请求,简单的意思就是在指定的时间内还未响应,停止请求 if (s.async && s.timeout > 0) { timeoutTimer = setTimeout(function () { - ->这里知道我们为什么设置timeout了吧,就是如果在timeout时间内还没请求到数据 jqXHR.abort("timeout"); 肯定得停止了,如何停止的,得看abort.提前是我们设置了这个参数并且是异步请求 }, s.timeout); } try { state = 1; transport.send(requestHeaders, done); -->这个方法就能知道jQuery是如何异步跨越请求这个js文件的,见下面分析 } catch (e) { // Propagate exception as error if not done if (state < 2) { -->处理上面这个方法的异常,万一你传得url不存在呢。。。 done(-1, e); // Simply rethrow otherwise } else { jQuery.error(e); } } } return jqXHR; -->分析到这里,怎么办了?其实看出来,我们只分析了$.ajax()并没用分析后面那段链式操作, }, 还有js文件怎么请求的,请求到哪儿了?先回到上面的done方法
上面代码的-->指出了这段代码关键的运行,以及为什么我们可以直接$.ajax().done().done()异步处理任务,而且可以同时增加多个回调函数,这多得多亏$.Deferred().
好吧,重点来分析怎么transport.send(requestHeaders,done)来跨域请求我们的js文件吧...怎么jQuery一个ajax这么复杂?还不是为了迎合上千万使用者不同的使用目的。也许对你来说,
你只需要一个简单的ajax请求,我才不管他跨域了没有!这里就是jQuery的一个缺点吧,木办法啊,你们怎么多使用者,我要每个都照顾啊!好吧,来分析js怎么请求的吧
/* 绑定script分发器,通过在header中创建script标签异步载入js. -->看到这里,你会有点了解这个transports分发器在干什么了, 谁叫你要请求js文件啊,还是跨域的,我平时就请求一个.php,没想这么多~_~, 原来ajax可以请求这么多东西 */ jQuery.ajaxTransport("script", function (s) { if (s.crossDomain) { -->我可是跨域请求哦 var script, head = document.head || document.getElementsByTagName("head")[0] || document.documentElement; //一步步的获取head元素 return { /*以下即是异步加载js的常用方法 */ send: function (_, callback) { -->尼玛的send方法在这里,藏得太深了 script = document.createElement("script"); -->好吧,看到这里,你觉得是不是突然jQuery里怎么会有这么简单的代码了? script.async = "async"; if (s.scriptCharset) { script.charset = s.scriptCharset; } script.src = s.url; // script加载完成触发的callback script.onload = script.onreadystatechange = function (_, isAbort) { -->js文件加载后的callback //中止script回调请求或者不存在readyState|| load|complete状态 if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { //处理ie下得内存泄露问题 ,onload事件触发后,销毁事件. script.onload = script.onreadystatechange = null; if (head && script.parentNode) { ->head.removeChild(script)?你把我才加载进来的js文件删了 干嘛啊,自己想吧 head.removeChild(script); //加载过后删除script标签元素 } // 注销script变量 script = undefined; ->真把加载进来的script清的一干而尽,看来只能跨域请求js,然后执行一次callback而已, ->有点划不来吧 ~_~ // 如果没有被中止调用callback if (!isAbort) { callback(200, "success"); ->这个callback是神马呢?就是上面的done方法,然后这个done方法里面根据不 同的status进行不同的请求,你没忘吧! } } }; //把创建的script加入head头部,但是加载完成后会被清除该script标签 head.insertBefore(script, head.firstChild); -->把我们请求的js文件加到头部,不加到头部没办法执行里面的函数 ~_~, 执行了又把人家删了 }, /*手动中止script请求 */ abort: function () { --> 好吧,我们没有abort if (script) { script.onload(0, 1); } } }; } });
这只是一个简单的跨域请求js文件,怎么这么多代码?哎,这个jQuery ajax的参数配置太多了,他会不停的if再if ,这些if造就了jQuery的调用接口这么灵活了,可以这么写,也可以那么写,满足不同
的人的口味。 分析到这里,可能你已经头很大了,木办法,它确实很难,这只是ajax的一部分。
好吧,我承认上面都是低级$.ajax方法,为了满足调用者使用更简单,作者提供了$.get() $.post(),$.getScript(),$.getJSON(),哎,一切为了减少使用者的负担啊,本来是没必要加这些代码的
还是来看看,这几个方法怎么基于$.ajax写出来的,其实很简单了,如果你真真看懂了$.ajax ~_~
/*这里遍历数组扩展$.get() $.post();其实都还是调用的$.ajax,只是type不同 * 这里$.ajax还是返回jqXHR,它与以前版本不同之处是它是超集的XMLHTTPRequest,由于deferred.promise(jqXHR);使这个jqXHR多了一些done,fail,always方法, * 当然也可以success,fail,complete其实这些方法就是done,fail,always,详情可见$.ajax和$.Deferred对象 */ jQuery.each(["get", "post"], function (i, method) { -->老生常谈得遍历数组,扩展$.method , 以后我一定帮你们分析下jQuery核心库代码^_^ jQuery[method] = function (url, data, callback, type) { //这里就是$.get("url",function(){})式的调用方法 if (jQuery.isFunction(data)) { //统一成$.get("url",data,function(){}) type = type || callback; callback = data; data = undefined; //把第二个参数设置成undefined,其目的也是是$.get()的接口灵活, //从这里可以看出jQuery的调用接口灵活还是基于强大的if判断语句 } //这里返回1.5版本之前的ajax调用方式 return jQuery.ajax({ -->搞来搞去还是$.ajax,只是帮你设置了参数了 type: method, //get or post url: url, data: data, success: callback, dataType: type }); }; }); getScript: function (url, callback) { ->尼玛的,还是调用的get,话说这个方法可以用了动态加载js文件,也可以按需加载哦, 相当于jQuery的js加载器吧,好扯的加载器, return jQuery.get(url, undefined, callback, "script"); }, //json文件,所有在JSON字符串表示,无论是属性或值,必须用双引号括起来,如{"key1":"value1","key2",:"value2"}保存在一个test.json文件中 //$.getJSON("test.json",function(data){$.each(data,function(key,value){})}); getJSON: function (url, data, callback) { return jQuery.get(url, data, callback, "json"); },
以上ajax代码分析了一半,就用了一个实例来分析,还有很多东西没讲,限于篇幅,下次吧,或者等我出书吧, 哎--> 我的梦想就是出书哎,然后教大家怎么更好的用jQuery。
顺便说下吧,我现在工资很低,没毕业,想挖我的公司联系我吧,哎,web前端js方向。
这也勉强算一个deferred应用吧,其实更多的是讲了ajax,以及跨域调用的问题.
下节应该是$(function(){}) $(funciton(){}) 为神马你的页面里面这么多这玩意,这玩意又是如何利用$.Deferred来完成的呢? 期待我有时间给大家讲吧 ^_^
相关文章推荐
- jQuery deferred应用之ajax详细源码分析(二)
- jQuery deferred应用dom加载完毕详细源码分析(三)
- jQuery Deferred对象详细源码分析(-)
- jQuery Deferred对象详细源码分析(-)
- jQuery源码分析-05异步队列 Deferred
- 《JavaScript 源码分析》之 jquery.unobtrusive-ajax.js
- jQuery源码分析之$.ajax方法
- jQuery1.9.1源码分析--Deferred对象和Promise对象
- jQuery-1.9.1源码分析系列(十六)ajax——ajax框架
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
- jQuery源码分析系列(30) : Ajax 整体结构
- jQuery-1.9.1源码分析系列(十六)ajax——ajax框架
- jQuery源码分析系列(36) : Ajax - 类型转化器
- jQuery-1.9.1源码分析系列(十六)ajax——jsonp原理
- jquery全局ajax参数详细执行分析-$.ajax竟然可以这样用!
- jquery全局ajax参数详细执行分析-$.ajax竟然可以这样用!
- jQuery1.9.1源码分析系列(十六)ajax之ajax框架
- jQuery-1.9.1源码分析系列(十六)ajax——ajax处理流程以及核心函数
- jquery Deferred异步回调源码解析应用
- jQuery源码分析之ajaxConverter与ajaxHandleResponse函数