Open toFrankie opened 1 year ago
接上一篇,继续介绍了 Promise 相关 API。
Promise.resolve() 方法的作用就是将某个值(非 Promise 对象实例)转换为 Promise 对象实例。
Promise.resolve()
Promise
const promise = Promise.resolve('foo') // 相当于 const promise = new Promise(resolve => resolve('foo'))
需要注意的是,它仍然会遵循 Event Loop 机制,包括后面介绍的其他 API。具体执行顺序本文不展开讨论。
Promise.resolve() 方法的参数分为四种情况:
它返回一个状态为 fulfilled,值为 undefined 的 Promise 实例对象。
fulfilled
undefined
const promise = Promise.resolve() // promise 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "fulfilled", // [[PromiseResult]]: undefined // }
这时,Promise.resolve() 将会不做任何修改、原封不动地返回该实例。
请注意,即使参数是一个 rejected 状态的 Promise 实例,返回的实例也不会变成 fulfilled 状态,不要被这个 resolve 字面意思误解了。
rejected
resolve
const p1 = new Promise(resolve => resolve({ name: 'Frankie' })) // "fulfilled" const p2 = new Promise((resolve, reject) => reject({ name: 'Frankie' })) // "rejected" const p3 = Promise.resolve(p1) const p4 = Promise.resolve(p2) console.log(p1 === p3) // true console.log(p2 === p4) // true // p3 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "fulfilled", // [[PromiseResult]]: { name: 'Frankie' } // } // p4 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "rejected", // [[PromiseResult]]: { name: 'Frankie' } // }
其实这种情况,就是上一篇提到过的。
const p5 = new Promise(resolve => resovle(1)) const p6 = new Promise(resolve => { reslove(p5) // 注意,不要尝试在此处调用 Promise.resolve(),会导致无限递归。 })
上面示例中,p6 的状态取决于 p5 的状态。
p6
p5
thenable 对象,是指具有 then 方法的对象。例如:
thenable
then
const obj = { then: function(resolve, reject) { resolve('foo') } }
上面示例中,obj 对象就是一个 thenable 对象。Promise.resolve() 方法会将这个 thenable 对象转为 Promise 对象,然后就立即执行 thenable 对象的 then() 方法。
obj
then()
const obj = { then: function (resolve, reject) { console.log(2) resolve('foo') // reject('foo') // 如果是这样,最终 promise 对象将会变成了 rejected 状态。 } } const promise = Promise.resolve(obj) promise.then(res => { console.log(3) console.log(res) // "foo" }) console.log(1) // 打印结果分别是 1、2、3、"foo" // promise 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "fulfilled", // [[PromiseResult]]: "foo" // }
上述示例中,obj 对象的 then() 方法执行后,对象 promise 的状态变成了 fulfilled,接着执行最后的那个 promise.then() 方法,打印出 "foo"。
promise
promise.then()
"foo"
如果是这种情况,Promise.resolve() 方法返回一个新的 Promise 对象,状态为 fulfilled,且该实例对象的值,就是该参数值。
const p1 = Promise.resolve('foo') const p2 = Promise.resolve({}) // p1 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "fulfilled", // [[PromiseResult]]: "foo" // } // p2 结果: // { // [[Prototype]]: Promise, // [[PromiseState]]: "fulfilled", // [[PromiseResult]]: {} // }
在实际项目中,一般是第 4 种情况居多,我似乎真的没见过前三种情况的。
Promise.reject() 方法会返回一个新的 Promise 实例对象,该实例的状态总是为 rejected。
Promise.reject()
const promise = Promise.reject('foo') // 相当于 const promise = new Promise((resolve, reject) => reject('foo'))
跟 Promise.resolve() 不同的是,Promise.reject() 方法的参数(无论是原始值、普通对象、还是 Promise 实例对象),将会原封不动地作为返回实例对象的值。
Promise.reject('Oops').catch(err => { console.log(err === 'Oops') // true // do something... })
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all()
const promise = Promise.all([p1, p2, p3])
上面代码中,Promise.all() 方法接受一个数组作为参数,其中 p1、p2、p3 都是 Promise 实例。如果数组中包含非 Promise 实例,它们会使用 Promise.resolve() 的方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all() 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p1
p2
p3
其中 promise 的状态由 p1、p2、p3 决定,分为两种情况。
只有当 p1、p2、p3 的状态都变成 fulfilled,promise 的状态才会变成 fulfilled,此时 p1、p2、p3 实例的值,会组成一个数组,并传递给 promise。
只要 p1、p2、p3 之中有一个被 rejected,promise 的状态就会变成 rejected。此时第一个 rejected 实例的值(注意,不会像上面一样组成数组哦),会传递给 promise。
看个例子:
const userIds = [1, 3, 5] const promiseArr = userIds.map(id => { return window.fetch(`/config/user/${id}`) // 假设是请求用户配置 }) Promise .all(promiseArr) .then(res => { // res 是一个数组,每一项对应每个实例的值,即 [[PromiseResult]] // 常见做法是将 res 进行解构,即 Promise.all(promiseArr).then(([a, b, c]) => { /* do something... */ }) // 假设 promiseArr 是一个空的可迭代对象,例如空数组,Promise.all([]) 实例状态为 fulfilled,值为 []。 // do something... }) .catch(err => { // err 为 Promise.all() 被 rejected 的原因(reason) })
上面的示例中,promiseArr 是包含 3 个 Promise 实例的数组,只有这 3 个实例的状态都变成 fulfilled,或其中一个变为 rejected,才会调用 Promise.all() 方法的回调函数。
promiseArr
Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.race()
const promise = Promise.race([p1, p2, p3])
Promise.race() 方法同样接受一个可迭代对象,只要 p1、p2、p3 中有一个实例率先改变状态(fulfiled 或 rejected),promise 的状态就会跟着改变,而且 promise 实例的值就是率先改变的实例的返回值。若可迭代对象中的某一项不是 Promise 实例,仍会使用 Promise.resolve() 进行转换。
fulfiled
当传递一个空的可迭代对象,那么 Promise.race() 实例的状态将会一直停留在 pending。这点跟 Promise.all() 是不同的。
pending
const p1 = Promise.all([]) const p2 = Promise.race([]) setTimeout(() => { console.log(p1) // Promise {<fulfilled>: Array(0)} console.log(p2) // Promise {<pending>} })
Promise.allSettled() 是 ES11 标准引入的一个方法,同样还是将多个 Promise 实例包装成一个新的 Promise 实例。只有等所有实例都返回结果(无论是 fulfilled 还 rejected),包装实例的状态才会发生变化。
Promise.allSettled()
我认为,这算是对 Promise.all() 存在 rejected 实例情况的一种补全吧。
注意,Promise.allSettled() 的状态,只可能是 pending 或 fulfilled 状态,不可能存在 rejected 状态。即只会从 pending -> fulfilled 的变化。
pending -> fulfilled
我们来看看以下示例,各种情况的结果吧:
const p1 = Promise.reject(1) const p2 = Promise.resolve(2) const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject(3) }, 1000) }) const p4 = new Promise(() => { }) // p4 状态将会一直停留在 pending const p5 = Promise.allSettled([]) // 参数为空迭代对象 const p6 = Promise.allSettled([p4]) const p7 = Promise.allSettled([p1, p2, p3]) setTimeout(() => { console.log('p1:', p1) console.log('p2:', p2) console.log('p3:', p3) console.log('p4:', p4) console.log('p5:', p5) console.log('p6:', p6) console.log('p7:', p7) p5.then(res => { console.log('p5 then:', res) }) p6.then(res => { // 这里将不会执行,因为 p6 一直处于 pending 状态 console.log('p6 then:', res) }) p7.then(res => { console.log('p7 then:', res) }) }, 2000)
列举以上示例,是为了得出以下结论:
Promise.allSettled() 一定要等到参数中每一个 Promise 状态定型后,它返回的实例对象才会定型为 fulfilled 状态。否则只会是 pending 状态。
类似 Promise.allSettled([]) 把一个空数组(空的迭代对象)作为参数,最后实例的状态为 fulfilled,且实例的值为空数组 []。
Promise.allSettled([])
[]
注意 Promise.allSettled() 返回的实例的值,首先它是一个数组,而数组每项都是一个对象,该对象的属性取决于对应参数 Promise 实例的状态。
例如 p1 的状态为 rejected,p2 的状态为 fulfilled。因此包装实例的前两项的对象分别为 { status: "rejected", reason: 1 }、{ status: "fulfilled", value: 2 },其他项同理。其中 status 属性只会是 fulfilled 或 rejected 两个字符串值。主要区别在于 value 属性和 reason 属性,即 fulfilled 状态对应 value 属性,而 rejected 状态对应 reason 属性。
{ status: "rejected", reason: 1 }
{ status: "fulfilled", value: 2 }
status
value
reason
有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,使用 Promise.allSettled() 方法就很有用了。而 Promise.all() 是没办法确保这一点的。
在 ES12 标准中,引入了 Promise.any() 方法,它用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.any()
Promise.any() 接受一个 Promise 可迭代对象,只要参数实例中有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态,其值就是参数实例的值。
Promise.any() 与 Promise.race() 很像,只有一个不同点,就是 Promise.any() 不会因为某个参数 Promise 实例变成 rejected 状态而接受,必须要等到所有参数实例的状态都变为 rejected,包装实例的状态才会是 rejected。
const p1 = Promise.reject(1) const p2 = Promise.resolve(2) const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject(3) }, 1000) }) const p4 = new Promise(() => { }) // p4 状态将会一直停留在 pending const p5 = Promise.any([]) // p5 会变成 rejected 状态 const p6 = Promise.any([p4]) const p7 = Promise.any([p1, p2, p3]) const p8 = Promise.any([p1, p3]) setTimeout(() => { console.log('p1:', p1) console.log('p2:', p2) console.log('p3:', p3) console.log('p4:', p4) console.log('p5:', p5) console.log('p6:', p6) console.log('p7:', p7) p5.then(res => { console.log('p5 then:', res) }).catch(err => { // p5 的状态会变成 rejected,因此会执行到这里。 console.log('p5 catch:', err) }) p6.then(res => { // p6 的状态一直会是 pending,因此不会执行回调。 console.log('p6 then:', res) }) p7.then(res => { console.log('p7 then:', res) }) p8.then(res => { console.log('p8 then:', res) }).catch(err => { // 注意 err 是一个对象 console.log('p8 catch:', err) console.dir(err) }) }, 2000)
当 Promise.any() 返回的实例变成 rejected 时,其实例的值是 AggregateError 实例。但传递一个空的迭代对象,Promise.any() 包装实例也会变成 rejected 状态,如 p5。
关于 Promise.all()、Promise.race()、Promise.allSettled()、Promise.any() 方法,总结以下特点。
它们的用处都是将多个 Promise 实例,包装成一个新的 Promise 实例。 它们都接受一个具有 Iterator 接口的可迭代对象,通常为数组。且会返回一个新的 Promise 实例对象。 它们处理参数为空的可迭代对象的方式不一样,本来就是要处理多个 Promise 对象,才会用到它们,所以这种情况无需理会。真遇到再回来翻阅文档即可,现在我写到这里都记不太清楚其中的区别了,但问题不大。 Promise.all() 当所有实例均为 fulfilled 状态,最终的包装实例才会是 fulfilled,其值是一个数组。否则将会是 rejected 状态; Promise.race() 则是某个实例的状态发生变化,最终包装实例将对应率先变化实例所对应的值和状态。“发生变化”是指 pending -> fulfilled 或 pending -> rejected。 Promise.allSettled() 单从命名上来猜测,就知道它需要等所有参数实例确定状态后,包装实例的状态才会变成 fulfilled 状态,注意它不存在 rejected 状态的情况。包装实例的返回值是一个数组,数组每项可能是 { status: "fulfilled", value: /* 对应 fulfilled 的值 */ } 或 { status: "rejected", reason: /* 对应 rejected 的原因 */ },取决于每个参数实例的状态。 Promise.any() 当某个参数实例的状态变为 fulfilled,那么包装实例就定型了,对应该参数实例的状态和值。否则它必须等到所有参数实例变为 rejected 状态,包装实例的状态才会发生改变,变为 rejected,其值是一个 AggregateError 实例。
它们的用处都是将多个 Promise 实例,包装成一个新的 Promise 实例。
它们都接受一个具有 Iterator 接口的可迭代对象,通常为数组。且会返回一个新的 Promise 实例对象。
它们处理参数为空的可迭代对象的方式不一样,本来就是要处理多个 Promise 对象,才会用到它们,所以这种情况无需理会。真遇到再回来翻阅文档即可,现在我写到这里都记不太清楚其中的区别了,但问题不大。
Promise.all() 当所有实例均为 fulfilled 状态,最终的包装实例才会是 fulfilled,其值是一个数组。否则将会是 rejected 状态;
Promise.race() 则是某个实例的状态发生变化,最终包装实例将对应率先变化实例所对应的值和状态。“发生变化”是指 pending -> fulfilled 或 pending -> rejected。
pending -> rejected
Promise.allSettled() 单从命名上来猜测,就知道它需要等所有参数实例确定状态后,包装实例的状态才会变成 fulfilled 状态,注意它不存在 rejected 状态的情况。包装实例的返回值是一个数组,数组每项可能是 { status: "fulfilled", value: /* 对应 fulfilled 的值 */ } 或 { status: "rejected", reason: /* 对应 rejected 的原因 */ },取决于每个参数实例的状态。
{ status: "fulfilled", value: /* 对应 fulfilled 的值 */ }
{ status: "rejected", reason: /* 对应 rejected 的原因 */ }
Promise.any() 当某个参数实例的状态变为 fulfilled,那么包装实例就定型了,对应该参数实例的状态和值。否则它必须等到所有参数实例变为 rejected 状态,包装实例的状态才会发生改变,变为 rejected,其值是一个 AggregateError 实例。
AggregateError
The end.
接上一篇,继续介绍了 Promise 相关 API。
一、Promise.resolve()
Promise.resolve()
方法的作用就是将某个值(非Promise
对象实例)转换为Promise
对象实例。Promise.resolve()
方法的参数分为四种情况:1. 不带任何参数
它返回一个状态为
fulfilled
,值为undefined
的Promise
实例对象。2. 参数是一个 Promise 实例对象
这时,
Promise.resolve()
将会不做任何修改、原封不动地返回该实例。请注意,即使参数是一个
rejected
状态的Promise
实例,返回的实例也不会变成fulfilled
状态,不要被这个resolve
字面意思误解了。其实这种情况,就是上一篇提到过的。
上面示例中,
p6
的状态取决于p5
的状态。3. 参数是一个 thenable 对象
thenable
对象,是指具有then
方法的对象。例如:上面示例中,
obj
对象就是一个thenable
对象。Promise.resolve()
方法会将这个thenable
对象转为Promise
对象,然后就立即执行thenable
对象的then()
方法。上述示例中,
obj
对象的then()
方法执行后,对象promise
的状态变成了fulfilled
,接着执行最后的那个promise.then()
方法,打印出"foo"
。4. 参数是一个不具有 then() 方法的对象,或者压根不是一个对象,而是原始值。
如果是这种情况,
Promise.resolve()
方法返回一个新的Promise
对象,状态为fulfilled
,且该实例对象的值,就是该参数值。在实际项目中,一般是第 4 种情况居多,我似乎真的没见过前三种情况的。
二、Promise.reject()
Promise.reject()
方法会返回一个新的Promise
实例对象,该实例的状态总是为rejected
。跟
Promise.resolve()
不同的是,Promise.reject()
方法的参数(无论是原始值、普通对象、还是Promise
实例对象),将会原封不动地作为返回实例对象的值。三、Promise.all()
Promise.all()
方法用于将多个Promise
实例,包装成一个新的Promise
实例。上面代码中,
Promise.all()
方法接受一个数组作为参数,其中p1
、p2
、p3
都是Promise
实例。如果数组中包含非Promise
实例,它们会使用Promise.resolve()
的方法,将参数转为Promise
实例,再进一步处理。另外,Promise.all()
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是Promise
实例。其中
promise
的状态由p1
、p2
、p3
决定,分为两种情况。只有当
p1
、p2
、p3
的状态都变成fulfilled
,promise
的状态才会变成fulfilled
,此时p1
、p2
、p3
实例的值,会组成一个数组,并传递给promise
。只要
p1
、p2
、p3
之中有一个被rejected
,promise
的状态就会变成rejected
。此时第一个rejected
实例的值(注意,不会像上面一样组成数组哦),会传递给promise
。看个例子:
上面的示例中,
promiseArr
是包含 3 个Promise
实例的数组,只有这 3 个实例的状态都变成fulfilled
,或其中一个变为rejected
,才会调用Promise.all()
方法的回调函数。四、Promise.race()
Promise.race()
方法同样是将多个Promise
实例,包装成一个新的Promise
实例。Promise.race()
方法同样接受一个可迭代对象,只要p1
、p2
、p3
中有一个实例率先改变状态(fulfiled
或rejected
),promise
的状态就会跟着改变,而且promise
实例的值就是率先改变的实例的返回值。若可迭代对象中的某一项不是Promise
实例,仍会使用Promise.resolve()
进行转换。当传递一个空的可迭代对象,那么
Promise.race()
实例的状态将会一直停留在pending
。这点跟Promise.all()
是不同的。五、Promise.allSettled()
Promise.allSettled()
是 ES11 标准引入的一个方法,同样还是将多个Promise
实例包装成一个新的Promise
实例。只有等所有实例都返回结果(无论是fulfilled
还rejected
),包装实例的状态才会发生变化。我认为,这算是对
Promise.all()
存在rejected
实例情况的一种补全吧。我们来看看以下示例,各种情况的结果吧:
列举以上示例,是为了得出以下结论:
Promise.allSettled()
一定要等到参数中每一个Promise
状态定型后,它返回的实例对象才会定型为fulfilled
状态。否则只会是pending
状态。类似
Promise.allSettled([])
把一个空数组(空的迭代对象)作为参数,最后实例的状态为fulfilled
,且实例的值为空数组[]
。注意
Promise.allSettled()
返回的实例的值,首先它是一个数组,而数组每项都是一个对象,该对象的属性取决于对应参数Promise
实例的状态。例如
p1
的状态为rejected
,p2
的状态为fulfilled
。因此包装实例的前两项的对象分别为{ status: "rejected", reason: 1 }
、{ status: "fulfilled", value: 2 }
,其他项同理。其中status
属性只会是fulfilled
或rejected
两个字符串值。主要区别在于value
属性和reason
属性,即fulfilled
状态对应value
属性,而rejected
状态对应reason
属性。有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,使用
Promise.allSettled()
方法就很有用了。而Promise.all()
是没办法确保这一点的。六、Promise.any()
在 ES12 标准中,引入了
Promise.any()
方法,它用于将多个Promise
实例,包装成一个新的Promise
实例。Promise.any()
接受一个Promise
可迭代对象,只要参数实例中有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态,其值就是参数实例的值。Promise.any()
与Promise.race()
很像,只有一个不同点,就是Promise.any()
不会因为某个参数Promise
实例变成rejected
状态而接受,必须要等到所有参数实例的状态都变为rejected
,包装实例的状态才会是rejected
。当
Promise.any()
返回的实例变成rejected
时,其实例的值是 AggregateError 实例。但传递一个空的迭代对象,Promise.any()
包装实例也会变成rejected
状态,如p5
。七、总结
关于
Promise.all()
、Promise.race()
、Promise.allSettled()
、Promise.any()
方法,总结以下特点。The end.