前言:在上一次的deferred对象学习中,对于then的用法并没有展开讨论,同时上一篇文章中说到“$.when()的参数只能是deferred对象”这一说法有误。
特此在本篇文章中更正when用法,同时对then用法展开讨论。
一、Promise对象与Deferred对象的区别
Promise对象是ES6的异步操作解决方案,为异步操作提供统一接口。它起到代理作用,充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口,可以让异步操作写起来,就像在写同步操作的流程。
Promise的用法:
根据ES6的规定,Promise对象是一个构造函数,可以生成Promise实例,构造函数接受一个回调函数,该回调函数的参数有两个,resolve和reject,均由JS引擎提供,不用自己部署;生成实例后,可以用then方法分别指定resolved和rejected状态的回调函数。
function asyncFunc() {
return new Promise(function(resolve, reject) {
// some async task
if (/*异步操作成功*/) {
resolve(value);
} else {
reject(err);
}
});
}
asyncFunc().then(function(value) {
//do something
}).catch(function(err) {
//do something
});
Deferred对象是jQuery用来统一处理回调函数的一个解决方案,在1.5.0版本开始引入。jQuery中的Deferred对象实现了Promise,并且将resolve和reject方法暴露在了构造函数外面,使得改变promise对象的状态更为灵活。
deferred.promise()是返回了一个纯粹的promise对象,即不能在外部调用resolve和reject方法。
Deferred的用法:
jQuery的所有Ajax操作函数,默认返回的就是一个deferred对象
var dfd = $.Deferred();
dfd.then(function(value) {
//do something
}).catch(function(err) {
//do something
});
二、promise.then()与deferred.then()的区别
promise.then()
then()返回一个新的promise对象,如果then()指定的回调函数有返回值,该返回值会作为参数,传入后面的回调函数。
deferred.then()
在jQuery 1.8之前,then()只是.done().fail()写法的语法糖,两种写法是等价的。
在jQuery 1.8之后,then()返回一个新的deferred对象,而done()返回的是原有的deferred对象。如果then()指定的回调函数有返回值,该返回值会作为参数,传入后面的回调函数。
(注:由于Deferred对象实现了Promise,为了便于描述,后面直接统称为Promise对象)
.catch()与then()的rejected回调作用几乎一致。但是由于Promise的抛错具有冒泡性质,能够不断传递,这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不要使用then()的rejected回调,而是统一使用catch()来处理错误。
当状态已经改变为resolved后,即使抛出错误,也不会触发then()的错误回调或者catch()方法。
then() 和 catch() 都会返回一个新的Promise对象,可以链式调用。
三、使用then()解决回调地狱
首先,看下Promise实例的异步方法中传参promise和then()中返回promise有什么区别
// p1异步方法中返回p2
let p1 = new Promise ( (resolve, reject) => {
resolve(p2)
} )
let p2 = new Promise ( ... )
p1.then(
//todo
alert('complete1');
).then(
//todo
alert('complete2');
)
// then()中返回promise
let p1 = new Promise ( (resolve, reject) => {
resolve()
} )
let p2 = new Promise ( ... )
p1.then(
alert('complete1');
() => return p2
).then(
//todo
alert('complete2');
)
p1异步方法中传参p2
p1的状态取决于p2,如果p2为pending,p1将等待p2状态的改变,p2的状态一旦改变,p1将会立即执行自己对应的回调,即第一个then()中的方法。
由于then()本身就会返回一个新的promise,所以第二个then()中的方法针对的是第一个then()本身返回的promise。
if p2 resolved --> p1 resolved --> complete1
then()中返回promise
如果在第一个then()中的方法手动返回p4,那么第二个then()中的方法针对的是p4,这样就可以在p4中再次通过 resolve() 和 reject() 来改变状态,第二个then()中的方法将等待p4状态的改变。
p1 resolved --> complete1 --> if p2 resolved --> complete2
由此可见,当then中的方法返回的是Promise对象时,会针对返回的Promise对象继续进行等待。这样可以更好地解决前端中的回调地狱问题(即回调嵌套)
举例:A B C D 这4个返回Promise对象的方法,想要按顺序进行异步操作,即A执行完了再执行B,B执行完了再执行C…,则:
A.then(B).then(C).then(D)
四、$.when()的用法
如果向 $.when 传入一个延迟对象,那么会返回它的 Promise 对象,可以继续绑定 Promise 对象的其它方法,例如 defered.then。
当延迟对象已经被受理resolved或被拒绝rejected(通常是由创建延迟对象的最初代码执行的),那么就会调用适当的回调函数。
例如,由 jQuery.ajax 返回的 jqXHR 对象是一个延迟对象,可以向下面这样使用:
$.when( $.ajax("test.aspx") ).then(function(data, textStatus, jqXHR){
alert( jqXHR.status ); // alerts 200
});
如果向 $.when 传入一个非延迟对象,那么它会被当作是一个被受理resolved的延迟对象,并且添加到上面的任何 doneCallbacks 都会被立刻执行,向 doneCallbacks 中传入的是原始的参数。
在这种情况下,设定的任何 failCallbacks 永远都不会被执行,因为延迟对象永远不会被拒绝rejected。例如:
$.when( { testing: 123 } ).done(
function(x) { alert(x.testing); } /* alerts "123" */
);
网友评论