then, catch, finally如何影响返回的Promise实例状态
原创@前端司南
虽然Promise是开发过程中使用非常频繁的一个技术点,但是它的一些细节可能很多人都没有去关注过。我们都知道,.then
, .catch
, .finally
都可以链式调用,其本质上是因为返回了一个新的Promise实例,而这些Promise实例现在的状态是什么或者将来会变成什么状态,很多人心里可能都没个底。我自己也意识到了这一点,于是我通过一些代码试验,发现了一些共性。如果您对这块内容还没有把握,不妨看看。
阅读本文前,您应该对Promise有一些基本认识,比如:
-
Promise
有pending
,fulfilled
,rejected
三种状态,其决议函数resolve()
能将Promise
实例的状态由pending
转为fulfilled
,其决议函数reject()
能将Promise
实例的状态由pending
转为rejected
。 -
Promise
实例的状态一旦转变,不可再逆转。
本文会从一些测验代码入手,看看Promise
的几个原型方法在处理Promise
状态时的一些细节,最后对它们进行总结归纳,加深理解!
先考虑then的行为
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
。
最后看看finally
不管一个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中抛出异常后会触发随后的catch
由于.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实例。
Promise和jQuery的链式调用区别在哪?
上文也提到了,.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;
}
- Hadoop数据分析平台实战——020Hadoop Shell命令(初学跳过)离线数据分析平台实战——020Hadoop Shell命令(可跳过)
- HDU 2080 夹角有多大II
- 二分查找模版
- Hadoop数据分析平台实战——010hadoop介绍安装
- Python为什么文件运行和在命令行运行同样语句但结果却不同?
- HDU 2034 人见人爱A-B
- Hadoop数据分析平台实战——030Hadoop Shell命令02(熟悉linux跳过)离线数据分析平台实战——030Hadoop Shell命令02
- Hadoop数据分析平台实战——070深入理解MapReduce 02(案例)离线数据分析平台实战——070深入理解MapReduce 02
- Hadoop数据分析平台实战——040HDFS介绍(熟悉基础概念跳过)离线数据分析平台实战——040HDFS&JAVA API(熟悉基础概念跳过)
- Hadoop数据分析平台实战——060深入理解MapReduce 01(案例)离线数据分析平台实战——060深入理解MapReduce 01(案例)
- 利用向量积(叉积)计算三角形的面积和多边形的面积
- HDU 1556 Color the ball
- Hadoop数据分析平台实战——080HBase介绍和安装离线数据分析平台实战——080HBase介绍和安装
- Hadoop数据分析平台实战——130Hive Shell命令介绍 02(熟悉Hive略过)离线数据分析平台实战——130Hive Shell命令介绍 02(熟悉Hive略过)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法