Deferred对象
摘要:虽然js已经出了ES6,ES7等等版本,从而也诞生了新的异步对象->promise,但是基础还是要终结的,这一片就来回顾一下ajax以及ajax的异步对象->deferred。
1.传统ajax
传统模式$.ajax()接受参数为一个对象,该对象包含了callback(success和error)
栗子:
1 function toJson(data) { 2 document.write('<pre><code>' + JSON.stringify(data) + '</code></pre>'); 3 } 4 $.ajax({ 5 url: "http://yapi.demo.qunar.com/mock/10343/getinfo", 6 success: function (data) { 7 toJson(data); 8 }, 9 error: function (error) { 10 console.log('error'); 11 } 12 });
这里安利一个第三方平台YAPI,可以通过YAPI模拟server端。
运行结果:
2.随着jQuery的改进,新的写法诞生了
栗子:
1 function toJson(data) { 2 document.write('<pre><code>' + JSON.stringify(data) + '</code></pre>'); 3 } 4 $.ajax("http://yapi.demo.qunar.com/mock/10343/getinfo") 5 .done(function(data){ 6 toJson(data); 7 }) 8 .fail(function(e){ 9 console.log(e); 10 })
运行结果:
增加了链式用法。这是为啥呢。因为jQuery在1.5.0版本以后增加了deferred对象,支持了链式调用方式。
3.增加了多个延迟函数公用一个回调函数,于是$.when()诞生了。
栗子:
1 function toJson(arr) { 2 arr.forEach(a => { 3 document.write('<pre><code>' + JSON.stringify(a) + '</code></pre>'); 4 }) 5 } 6 $.when($.ajax("http://yapi.demo.qunar.com/mock/10343/getinfo"), 7 $.ajax("http://yapi.demo.qunar.com/mock/10343/getinfo")) 8 .done(function (data, data2) { 9 toJson([data[0], data2[0]]); 10 }) 11 .fail(function (e) { 12 console.log(e); 13 })
结果:
这里如果两个ajax如果其中有一个失败则会调用fail的callback,到这里如果异步不是ajax呢,是setTimeout呢~
栗子:
1 function wait() { 2 var delayFn = function () { 3 console.log('over.'); 4 }; 5 setTimeout(delayFn, 5000); 6 };
要知道执行函数wait的时候,delayFn会以一个新的宏任务从新扔到一个事件队列里,这时两个函数变成为了异步,那么这时候出现一个新的函数,如何控制新的函数要执行与延迟函数之后呢。
方式1:讲函数写在延迟函数内,这不废话吗。
方式2:如果与wait在同属一个宏任务呢?答案是利用$.when(),以微任务的形式存在。这样等延迟函数执行完再去执行,就是我们上面栗子3的形式了。
栗子:
1 function wait(dtd) { 2 console.log(new Date()) 3 var delayFn = function () { 4 }; 5 setTimeout(delayFn, 5000); 6 }; 7 $.when(wait()) 8 .done(function (data) { 9 console.log(new Date()); 10 }) 11 .fail(function (e) { 12 console.log(e) 13 })
执行结果:
事实上并没有等延迟函数执行完,才进入的回调函数,似乎直接就执行了回调函数,这是为啥呢?
原因是:$.when()接受的对象必须是deferred对象,否则会立即执行回调函数。改进如下。
栗子:
1 var dtd = $.Deferred(); 2 function wait(dtd) { 3 console.log(new Date()) 4 var delayFn = function () { 5 dtd.resolve(); 6 }; 7 setTimeout(delayFn, 5000); 8 return dtd; 9 }; 10 $.when(wait(dtd)) 11 .done(function (data) { 12 console.log(new Date()); 13 }) 14 .fail(function (e) { 15 console.log(e) 16 })
运行结果:
deferred对象此外还有两个方法分别是:reject()、promise()
resolve:改变deferred对象的状态,执行done callback。
reject:reject()与reslove()方法一样,同为改变deferred对象的状态,当状态为reject时,这执行fail callback。
promise:重新生成deferred对象。
4.promise对象
我猜ES6也是根据这个词儿诞生的promise对象,嘿嘿。
上面栗子可以看到dtd是外部传入的对象,在wait以外就可以改变deferred对象状态,直接执行回调函数
栗子:
1 function toJson(arr) { 2 arr.forEach(a => { 3 document.write('<pre><code>' + JSON.stringify(a) + '</code></pre>'); 4 }) 5 } 6 var dtd = $.Deferred(); 7 function wait(dtd) { 8 console.log(new Date()) 9 var delayFn = function () { 10 dtd.resolve(); 11 }; 12 setTimeout(delayFn, 5000); 13 return dtd; 14 }; 15 $.when(wait(dtd)) 16 .done(function (data) { 17 console.log(new Date()); 18 }) 19 .fail(function (e) { 20 console.log('error' + ' time:' + new Date()) 21 }) 22 dtd.reject();
执行结果:
可以看到在外面修改deferred状态就会立即执行fail callback函数了。那么如何避免呢~,我们利用promise() 重新生成一个deferred对象。
栗子:
1 function toJson(arr) { 2 arr.forEach(a => { 3 document.write('<pre><code>' + JSON.stringify(a) + '</code></pre>'); 4 }) 5 } 6 var dtd = $.Deferred(); 7 var waitDtd = function wait(dtd) { 8 console.log(new Date()) 9 var delayFn = function () { 10 dtd.resolve(); 11 }; 12 setTimeout(delayFn, 5000); 13 return dtd.promise(); 14 }; 15 $.when(waitDtd) 16 .done(function (data) { 17 console.log(new Date()); 18 }) 19 .fail(function (e) { 20 console.log('error' + ' time:' + new Date()) 21 }) 22 waitDtd.reject();
over~