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