Promise
算是 JavaScript
异步编程演进过程中的里程碑级别的特性更新
最早由社区提出和实现,直到 ES6
发布将其写进了标准,统一了用法,并原生提供了 Promise
对象
- 通过
then
函数的链式调用方式,在形式上初步解决了回调地狱的问题 - 成为后续
async/await
、fetch
等等API的基石
Promise 特性
- 异步任务
- 值穿透
-
resolve/reject
没有中断执行 -
catch
和then rejectHandle
的区别
异步任务
-
new Promise
是同步执行的 -
Promise
的then
catch
finally
才是异步执行的
// 下方代码输出顺序: 1 -> 3 -> 2 -> 4
console.log(1)
new Promise((resolve) => {
console.log(3)
resolve(4)
})
.then((data) => {
console.log(data)
})
console.log(2)
值穿透
Promise
的 then
catch
的参数期望是函数,传入非函数则会发生值穿透
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log) // 1
resolve/reject 没有中断执行
resolve/reject
只是改变了 Promise
对象的状态,并没有中断当前的执行
我们有时候会下意识的认为:resolve/reject
直接让程序进入了下一个阶段,其实并不是
// 下方代码输出顺序: 1 -> 3 -> 2
new Promise((resolve) => {
console.log(1)
resolve(2)
console.log(3)
})
.then((data) => {
console.log(data)
})
catch 和 then rejectHandle 的区别
-
catch
可以捕获Promise resolve
之前的throw Error
错误 -
then rejectHandler
不能捕获同级then resolveHandler
中的错误
new Promise((resolve) => {
resolve(1)
throw new Error('err') // 不会进入 Promise catch
})
new Promise((resolve) => {
resolve()
})
.then(() => {
throw new Error('err')
}, () => {
// 无法捕获同级 then rejectHandler 中的错误 err
})
Promise 静态方法特性
Promise.all
Promise.all
参数接收一个可迭代对象(一般是数组)并返回一个新的 Promise
对象
具体参考 MDN: Promise.all()
- 如果参数是空数组,进入
then
- 如果参数数组不包含
Promise
对象,进入 then,例如Promise.all([1,2,3])
- 如果一个元素
Promise reject
,会立刻进入Promise.all
的catch
,但不会影响其他元素 Promise 的继续执行 - 如果想知道是哪一个失败了,可以在元素
Promise
的reject
中标识
如果数组元素 Promise
自己处理内部错误并重新返回一个 resolve
的 Promise
,就不会因此导致进入 Promise.all
的 catch
Promise.race
Promise.race
参数和 Promise.all
一样
具体参考 MDN: Promise.race()
- 如果参数是空数组,
then
catch
都不执行 - 如果数组非空,根据第一个返回的元素
Promise
的resolve
reject
,立刻对应进入Promise.race
的then
catch
- 无论第一个元素
Promise
的结果如何,不会影响其他元素Promise
继续执行
Promise.allSettled
Promise.allSettled
由 ES11
引入标准
Promise.allSettled
会等待全部元素 Promise
完成后(无论 resolve
还是 reject
),进入 then
那什么情况下能进入 Promise.allSettled
的 catch
呢?
在某个 Promise
throw Error
的时候!
并且 Promise.allSettled
的 catch
会捕获每一个 Promise
中的错误,因此有可能执行多次
(Promise.all
只会捕捉第一个 reject
的错误)
const promise1 = new Promise(function(resolve, reject) {
setTimeout(() => {
throw Error(1)
}, 100)
});
const promise2 = new Promise(function(resolve, reject) {
setTimeout(() => {
throw Error(2)
}, 200)
});
Promise.allSettled([promise1, promise2])
.then((value) => {
console.log('then', value)
})
.catch((err) => {
// 100ms 后捕获 promise1 的错误,200ms 后捕获 promise2 的错误
console.log('catch', err)
})
Promise.any
Promise.any
由 ES12
引入标准
- 如果元素
Promise
有一个resolve
,就进入Promise.any
的then
- 如果元素
Promise
全部都reject
,就进入Promise.any
的catch
Promise/A+ 规范
如上文所述,Promise
是 ES6
提供的标准原生对象
当我们的代码需要编译成 ES5
或者以下版本的时候,需要引入 polyfill
polyfill 中的 Promise 就是根据 Promise/A+ 规范编写的
详情参考:Promise/A+ 规范
- 一个
Promise
的当前状态必须为以下三种状态中的一种:等待态(Pending
)、执行态(Fulfilled
)和拒绝态(Rejected
)- 处于等待态时,
promise
可以迁移至执行态或拒绝态 - 处于执行态时,
promise
不能迁移至其他任何状态,必须拥有一个不可变的终值 - 处于拒绝态时,
promise
不能迁移至其他任何状态,必须拥有一个不可变的据因
- 处于等待态时,
- 必须实现 then 方法
-
then
函数接受两个参数 -
then
函数参数如果不是函数,则忽略该参数 -
then
函数可以被一个promise
多次调用 - 必须返回一个
promise
实例
-
写在最后
总有人和我说 Promise.all
和 Promise.race
其中一个 Promise
reject
会中断其他 Promise
的进行
我查阅文档 + 写 demo
测试,都证明既不中断,也不等待
如有同样认为会中断的兄弟,希望在评论区给个 demo