在异步机制这章我们详细的分析了 deferred 的设计,其中提供了 deferred.promise 方法就是把普通对象转化成 deferred 对象了,ajax 就是把 deferred 对象给掺进去可以让整个 Ajax 方法变成了一个 deferred 对象,在Ajax方法中返回的是 jqXHR 一个包装对象,在这个对象里面混入了所有实现方法。
ajax: function(url, options) { var jqXHR = {} //ajax对象 deferred = jQuery.Deferred() //转成deferred对象 deferred.promise(jqXHR).complete = completeDeferred.add return jqXHR }
jQuery.ajax 的版本迭代:
为了向后兼容 XMLHttpRequest ,jqXHR 对象将公开下列属性和方法:
readyState status statusText responseXML and/or responseText 当底层的请求分别作出XML和/或文本响应 setRequestHeader(name, value) 从标准出发,通过替换旧的值为新的值,而不是替换的新值到旧值 getAllResponseHeaders() getResponseHeader() abort()
为了实现以上这些功能,jQuery 在对 jqXHR 做2个处理:
// Deferreds deferred = jQuery.Deferred(), //所有的回调队列,不管任何时候增加的回调保证只触发一次 completeDeferred = jQuery.Callbacks("once memory"),
给 jqXHR 扩充添加 promise 的属性和方法,然后添加 complete 方法,这里用的是回调列表的 add 方法(即添加回调)
deferred.promise(jqXHR).complete = completeDeferred.add;
此时的 jqXHR 就具有了 promise 的一些特性了与 callback 的回调列队了,当然这里有个重点,返回了一个只读的 deferred 对象,如果返回完整的 deferred 对象,那么外部程序就能随意的触发 deferred 对象的回调函数,很有可能在 AJAX 请求结束前就触发了回调函数(resolve),这就是与 AJAX 本身的逻辑相违背了。所以为了避免不经意间改变任务的内部流程,我们应该只返回 deferred 的只读版本 deferred.promise(),然后把对应的 done 与 fail 改成别名 success 与 error。
jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail
我们还需要把用户自定的内部回调函数给注册到 jqXHR 对象上。
// 增加回调队列 for (i in { success : 1, error : 1, complete : 1 }) { /** * 把参数的回调函数注册到内部jqXHR对象上,实现统一调用 * 给ajax对象注册 回调函数add * deferred返回complete,error外部捕获 */ jqXHR[i](s[i]); }
通过一个 for 循环把对应的方法都执行了,具体就是这几个:
我们参考右边 Ajax 的模拟实现代码。
<!doctype html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> <title>模拟ajax的 script请求</title> <script src="http://code.jquery.com/jquery-latest.js"></script> </head> <body> <button id="test">模拟jQuery.ajax的script请求</button> <script type="text/javascript"> /** * 模拟ajax的 script请求 * @param {[type]} options [description] * @return {[type]} [description] */ function createAjax(options) { if (typeof url === "object") { options = url; url = undefined; } options = options || {}; /** * 参数 * jQuery.ajaxSetup 是默认参数 * @type {[type]} */ var s = jQuery.ajaxSetup({}, options); // Deferreds // 异步机制 var deferred = jQuery.Deferred(); var completeDeferred = jQuery.Callbacks("once memory"); /** * 实际返回的ajax对象 * @type {Object} */ var jqXHR = {} // 把jqXHR对象转化promise对象,幷加入complete、success、error方法 deferred.promise(jqXHR).complete = completeDeferred.add; //别名 jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; // 增加回调队列 // complete: function() { // console.log('局部事件complete') // }, // error: function() { // console.log('局部事件error请求失败时调用此函数') // }, // success: function() { // console.log('局部事件success') // } for (i in { success: 1, error: 1, complete: 1 }) { jqXHR[i](s[i]); } function send(_, complete) { var script = jQuery("<script>").prop({ async: true, charset: s.scriptCharset, src: s.url }).on( "load error", callback = function(evt) { script.remove(); callback = null; if (evt) { complete(evt.type === "error" ? 404 : 200, evt.type); } } ); document.head.appendChild(script[0]); } function done(status, nativeStatusText, responses, headers) { var isSuccess = status >= 200 && status < 300 || status === 304; var success = jqXHR.success; var error = jqXHR.error; if (isSuccess) { deferred.resolveWith(document, [success, jqXHR]); } else { deferred.rejectWith(document, [jqXHR, error]); } } //发送请求 send({ Accept: "text/javascript, application/javascript, application/ecmascri" }, done); return jqXHR; } function show(data){ $('body').append('<li>'+ data +'</li>'); } $("#test").click(function(){ //执行一个异步的HTTP(Ajax)的请求。 var ajax = createAjax({ url: 'http://code.jquery.com/jquery-latest.js', dataType: 'script', //请求完成后回调函数 (请求success 和 error之后均调用) complete: function() { show('局部事件complete') }, error: function() { show('局部事件error请求失败时调用此函数') }, success: function() { show('局部事件success') } }) ajax.done(function() { show('deferred done') }).fail(function() { show('deferred fail') }).always(function() { show('deferred lways') }) }) </script> </body> </html>