这是我参与更文挑战的第3天,活动详情查看:更文挑战
Promise 算是 JavaScript 异步编程演进过程中的里程碑级别的特性更新
最早由社区提出和实现,直到 ES6 发布将其写进了标准,统一了用法,并原生提供了 Promise 对象
- 通过
then函数的链式调用方式,在形式上初步解决了回调地狱的问题 - 成为后续
async/await、fetch等等API的基石
Promise 特性
- 异步任务
- 值穿透
resolve/reject没有中断执行catch和then rejectHandle的区别
异步任务
new Promise是同步执行的Promise的thencatchfinally才是异步执行的
// 下方代码输出顺序: 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()
- 如果参数是空数组,
thencatch都不执行 - 如果数组非空,根据第一个返回的元素
Promise的resolvereject,立刻对应进入Promise.race的thencatch - 无论第一个元素
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