then, catch, finally如何影响返回的Promise实例状态
共 7306字,需浏览 15分钟
·
2020-12-19 15:22
原创@前端司南
虽然Promise是开发过程中使用非常频繁的一个技术点,但是它的一些细节可能很多人都没有去关注过。我们都知道,.then
, .catch
, .finally
都可以链式调用,其本质上是因为返回了一个新的Promise实例,而这些Promise实例现在的状态是什么或者将来会变成什么状态,很多人心里可能都没个底。我自己也意识到了这一点,于是我通过一些代码试验,发现了一些共性。如果您对这块内容还没有把握,不妨看看。
阅读本文前,您应该对Promise有一些基本认识,比如:
Promise
有pending
,fulfilled
,rejected
三种状态,其决议函数resolve()
能将Promise
实例的状态由pending
转为fulfilled
,其决议函数reject()
能将Promise
实例的状态由pending
转为rejected
。Promise
实例的状态一旦转变,不可再逆转。
本文会从一些测验代码入手,看看Promise
的几个原型方法在处理Promise
状态时的一些细节,最后对它们进行总结归纳,加深理解!
then
的语法形式如下:
p.then(onFulfilled[, onRejected]);
onFulfilled
可以接受一个value
参数,作为Promise
状态决议为fulfilled
的结果,onRejected
可以接受一个reason
参数,作为Promise
状态决议为rejected
的原因。
- 如果
onFulfilled
或onRejected
不返回值,那么.then
返回的Promise
实例的状态会变成fulfilled
,但是伴随fulfilled
的value
会是undefined
。
new Promise((resolve, reject) => {
resolve(1)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
- 如果
onFulfilled
或onRejected
返回一个值x
,那么.then
返回的Promise
实例的状态会变成fulfilled
,并且伴随fulfilled
的value
会是x
。注意,一个非Promise
的普通值在被返回时会被Promise.resolve(x)
包装成为一个状态为fulfilled
的Promise
实例。
new Promise((resolve, reject) => {
reject(2)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
return 'a new value'
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
- 如果
onFulfilled
或onRejected
中抛出一个异常,那么.then
返回的Promise
实例的状态会变成rejected
,并且伴随rejected
的reason
是刚才抛出的异常的错误对象e
。
new Promise((resolve, reject) => {
resolve(1)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
throw new Error('some error occurred.')
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
- 如果
onFulfilled
或onRejected
返回一个Promise
实例p2
,那么不管p2
的状态是什么,.then
返回的新Promise
实例p1
的状态会取决于p2
。如果p2
现在或将来是fulfilled
,那么p1
的状态也随之变成fulfilled
,并且伴随fulfilled
的value
也与p2
进行resolve(value)
决议时传递的value
相同;
new Promise((resolve, reject) => {
resolve(1)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
return Promise.resolve('a fulfilled promise')
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
这个逻辑同样适用于rejected
的场景。也就是说,如果p2
的状态现在或将来是rejected
,那么p1
的状态也随之变成rejected
,而reason
也来源于p1
进行reject(reason)
决议时传递的reason
。
new Promise((resolve, reject) => {
reject(1)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('a promise rejected after 3 seconds.')
}, 3000)
})
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
再考虑catch的行为catch的语法形式如下:
p.catch(onRejected);
.catch
只会处理rejected
的情况,并且也会返回一个新的Promise
实例。
.catch(onRejected)
与then(undefined, onRejected)
在表现上是一致的。
事实上,catch(onRejected)从内部调用了then(undefined, onRejected)。
- 如果
.catch(onRejected)
的onRejected
回调中返回了一个状态为rejected
的Promise
实例,那么.catch
返回的Promise
实例的状态也将变成rejected
。
new Promise((resolve, reject) => {
reject(1)
}).catch(reason => {
console.log('rejection occurred, and the reason is: ', reason)
return Promise.reject('rejected')
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
- 如果
.catch(onRejected)
的onRejected
回调中抛出了异常,那么.catch
返回的Promise
实例的状态也将变成rejected
。
new Promise((resolve, reject) => {
reject(1)
}).catch(reason => {
console.log('rejection occurred, and the reason is: ', reason)
throw 2
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
- 其他情况下,
.catch
返回的Promise
实例的状态将是fulfilled
。
不管一个Promise
的状态是fulfilled
还是rejected
,传递到finally
方法的回调函数onFinally
都会被执行。我们可以把一些公共行为放在onFinally
执行,比如把loading
状态置为false
。
注意,onFinally
不会接受任何参数,因为它从设计上并不关心Promise
实例的状态是什么。
p.finally(function() {
// settled (fulfilled or rejected)
});
finally
方法也会返回一个新的Promise
实例,这个新的Promise
实例的状态也取决于onFinally
的返回值是什么,以及onFinally
中是否抛出异常。
你可以通过修改以下代码中的注释部分来验证,不同的返回值对于finally
返回的Promise
实例的状态的影响。
new Promise((resolve, reject) => {
reject(1)
}).then(value => {
console.log('resolution occurred, and the value is: ', value)
}, reason => {
console.log('rejection occurred, and the reason is: ', reason)
return Promise.resolve(2);
// return Promise.reject(3)
}).finally(() => {
// return Promise.resolve(4)
// return Promise.reject(5)
throw new Error('an error')
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
}, reason => {
console.log('rejection of the returned promise occurred, and the reason is: ', reason)
})
then, catch, finally小结综合以上来看,不管是.then(onFulfilled, onRejected)
,还是.catch(onRejected)
,或者是.finally(onFinally)
,它们返回的Promise
实例的状态都取决于回调函数是否抛出异常,以及返回值是什么。
如果回调函数的返回值是一个状态为
rejected
的Promise
实例,那么.then
,.catch
或.finally
返回的Promise
实例的状态就是rejected
。如果回调函数的返回值是一个还未决议的
Promise
实例p2
,那么.then
,.catch
或.finally
返回的Promise
实例p1
的状态取决于p2
的决议结果。如果回调函数中抛出了异常,那么
.then
,.catch
或.finally
返回的Promise
实例的状态就是rejected
,并且reason
是所抛出异常的对象e
。其他情况下,
.then
,.catch
或.finally
返回的Promise
实例的状态将是fulfilled
。
由于.then
会返回一个新的Promise
实例,而在.then
回调中抛出了异常,导致这个新Promise
的状态变成了rejected
,而.catch
正是用于处理这个新的Promise
实例的rejected
场景的。
new Promise((resolve, reject) => {
resolve(1)
}).then(value => {
console.log('resolution of the returned promise occurred, and the value is: ', value)
var a = b; // 未定义b
}).catch(reason => {
console.log('caught the error occured in the callback of then method, and the reason is: ', reason)
})
最关键一点就是要理解:每次.then
, .catch
, .finally
都产生一个新的Promise实例。
上文也提到了,.then
, .catch
, .finally
都产生一个新的Promise实例,所以这种链式调用的对象实例已经发生了变化。可以理解为:
Promise.prototype.then = function() {
// balabala
return new Promise((resolve, reject) => {
// if balabala
// else if balabala
// else balabala
});
}
而jQuery链式调用是基于同一个jQuery实例的,可以简单表述为:
jQuery.fn.css = function() {
// balabala
return this;
}
❤️ 感谢支持
本文主要是参考了MDN和《你不知道的JavaScript(下卷)》上关于Promise的知识点,简单分析了.then
, .catch
, .finally
中回调函数的不同行为对于三者返回的Promise实例的影响,希望对大家有所帮助。
- 收藏吃灰不如现在就开始学习,奥利给!
- 如果您觉得本文有所帮助,请留下您的点赞关注支持一波,谢谢!
- 快关注公众号前端司南,与笔者一起交流学习吧!