0. 前言
面试官:「你写个 Promise 吧。」
我:「对不起,打搅了,再见!」
当初前端越来越卷,不会手写 Promise
都不好意思面试了(手动狗头.jpg)。尽管没多少人会在业务中用本人实现的 Promise
,然而,实现 Promise
的过程会让你对 Promise
更加理解,出了问题也能够更好地排查。
如果你还不相熟 Promise,倡议先看一下 MDN 文档。
在实现 Promise 之前,我倡议你先看一遍 Promises/A+ 标准(中文翻译:Promise A+ 标准),本文中不会再次介绍相干的概念。举荐大家优先浏览原版英文,只需高中程度的英语常识就够了,遇到不懂的再看译文。
另外,本文将应用 ES6 中的 Class 来实现 Promise
。为了不便大家跟 Promise/A+ 标准对照着看,下文的程序将依照标准的程序来行文。
在正式开始之前,咱们新建一个我的项目,名称随便,依照以下步骤进行初始:
- 关上 CMD 或 VS Code,运行
npm init
,初始化我的项目 - 新建
PromiseImpl.js
文件,前面所有的代码实现都写在这个文件里
残缺代码地址:ashengtan/promise-aplus-implementing
1. 术语
这部分大家间接看标准就好,也没什么好解释的。留神其中对于 value
的形容,value
能够是一个 thenable
(有 then
办法的对象或函数)或者 Promise
,这点会在前面的实现中体现进去。
2. 要求
2.1 Promise 的状态
一个 Promise
有三种状态:
pending
:初始状态fulfilled
:胜利执行rejected
:拒绝执行
一个 Promise
一旦从 pending
变为 fulfilled
或 rejected
,就无奈变成其余状态。当 fulfilled
时,须要给出一个不可变的值;同样,当 rejected
时,须要给出一个不可变的起因。
依据以上信息,咱们定义 3 个常量,用来示意 Promise
的状态:
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
接着,咱们先把 Promise
的根底框架先定义进去,这里我应用 ES6 的 Class 来定义:
class PromiseImpl {constructor() {}
then(onFulfilled, onRejected) {}}
这里咱们先回忆一下 Promise
的根本用法:
const promise = new Promise((resolve, reject) => {
// ...do something
resolve(value) // or reject(error)
})
// 屡次调用
const p1 = promise.then()
const p2 = promise.then()
const p3 = promise.then()
好了,持续欠缺 PromiseImpl
,先欠缺一下构造方法:
class PromiseImpl {constructor() {
// `Promise` 以后的状态,初始化时为 `pending`
this.status = STATUS_PENDING
// fulfilled 时的值
this.value = null
// rejected 时的起因
this.reason = null
}
}
另外,咱们还要定义两个办法,用于 fulfilled
和 rejected
时回调:
class PromiseImpl {constructor() {
// ... 其余代码
// 2.1.2 When `fulfilled`, a `promise`:
// 2.1.2.1 must not transition to any other state.
// 2.1.2.2 must have a value, which must not change.
const _resolve = value => {
// 如果 `value` 是 `Promise`(即嵌套 `Promise`),// 则须要期待该 `Promise` 执行实现
if (value instanceof PromiseImpl) {
return value.then(value => _resolve(value),
reason => _reject(reason)
)
}
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
}
}
// 2.1.3 When `rejected`, a `promise`:
// 2.1.3.1 must not transition to any other state.
// 2.1.3.2 must have a reason, which must not change.
const _reject = reason => {if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
}
}
}
}
留神,在 _resolve()
中,如果 value
是 Promise
的话(即嵌套 Promise
),则须要期待该 Promise
执行实现。这点很重要,因为前面的其余 API 如 Promise.resolve
、Promise.all
、Promise.allSettled
等均须要期待嵌套 Promise
执行实现才会返回后果。
最初,别忘了在 new Promise()
时,咱们须要将 resolve
和 reject
传给调用者:
class PromiseImpl {constructor(executor) {
// ... 其余代码
try {executor(_resolve, _reject)
} catch (e) {_reject(e)
}
}
}
应用 trycatch
将 executor
包裹起来,因为这部分是调用者的代码,咱们无奈保障调用者的代码不会出错。
2.2 Then 办法
一个 Promise
必须提供一个 then
办法,其承受两个参数:
promise.then(onFulfilled, onRejected)
class PromiseImpl {then(onFulfilled, onRejected) {}}
2.2.1 onFulfilled
和 onRejected
从标准 2.2.1 中咱们能够得悉以下信息:
onFulfilled
和onRejected
是可选参数- 如果
onFulfilled
和onRejected
不是函数,则必须被疏忽
因而,咱们能够这样实现:
class PromiseImpl {then(onFulfilled, onRejected) {
// 2.2.1 Both `onFulfilled` and `onRejected` are optional arguments:
// 2.2.1.1 If `onFulfilled` is not a function, it must be ignored
// 2.2.1.2 If `onRejected` is not a function, it must be ignored
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {}
onRejected = typeof onRejected === 'function' ? onRejected : () => {}
}
}
2.2.2 onFulfilled
个性
从标准 2.2.2 中咱们能够得悉以下信息:
- 如果
onFulfilled
是一个函数,则必须在fulfilled
后调用,第一个参数为promise
的值 - 只能调用一次
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
// 2.2.2 If `onFulfilled` is a function:
// 2.2.2.1 it must be called after `promise` is fulfilled,
// with promise’s value as its first argument.
// 2.2.2.2 it must not be called before `promise` is fulfilled.
// 2.2.2.3 it must not be called more than once.
if (this.status === STATUS_FULFILLED) {onFulfilled(this.value)
}
}
}
2.2.3 onRejected
个性
与 onFulfilled
同理,只不过是在 rejected
时调用,第一个参数为 promise
失败的起因
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
// 2.2.3 If onRejected is a function:
// 2.2.3.1 it must be called after promise is rejected,
// with promise’s reason as its first argument.
// 2.2.3.2 it must not be called before promise is rejected.
// 2.2.3.3 it must not be called more than once.
if (this.status === STATUS_REJECTED) {onRejected(this.reason)
}
}
}
2.2.4 异步执行
在日常开发中,咱们常常应用 Promise
来做一些异步操作,标准 2.2.4 就是规定异步执行的问题,具体的能够联合标准里的正文浏览,重点是确保 onFulfilled
和 onRejected
要异步执行。
须要指出的是,标准里并没有规定 Promise
肯定要用 micro-task
机制来实现,因而你应用 macro-task
机制来实现也是能够的。当然,当初浏览器用的是 micro-task
来实现。这里为了不便,咱们应用 setTimeout
(属于 macro-task
)来实现。
因而,咱们须要略微革新下下面的代码:
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
// fulfilled
if (this.status === STATUS_FULFILLED) {setTimeout(() => {onFulfilled(this.value)
}, 0)
}
// rejected
if (this.status === STATUS_REJECTED) {setTimeout(() => {onRejected(this.reason)
}, 0)
}
}
}
2.2.5 onFulfilled
和 onRejected
必须作为函数被调用
这个曾经在下面实现过了。
2.2.6 then
可被屡次调用
举个例子:
const promise = new Promise((resolve, reject) => {
// ...do something
resolve(value) // or reject(error)
})
promise.then()
promise.then()
promise.catch()
因而,必须确保当 Promise
fulfilled
或 rejected
时,onFulfilled
或 onRejected
依照其注册的程序逐个回调。还记得最开始咱们定义的 resolve
和 reject
吗?这里咱们须要革新下,保障所有的回调都被执行到:
const invokeArrayFns = (fns, arg) => {for (let i = 0; i < fns.length; i++) {fns[i](arg)
}
}
class PromiseImpl {constructor(executor) {
// ... 其余代码
// 用于寄存 `fulfilled` 时的回调,一个 `Promise` 对象能够注册多个 `fulfilled` 回调函数
this.onFulfilledCbs = []
// 用于寄存 `rejected` 时的回调,一个 `Promise` 对象能够注册多个 `rejected` 回调函数
this.onRejectedCbs = []
const resolve = value => {if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
// 2.2.6.1 If/when `promise` is fulfilled,
// all respective `onFulfilled` callbacks must execute
// in the order of their originating calls to `then`.
invokeArrayFns(this.onFulfilledCbs, value)
}
}
const reject = reason => {if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
// 2.2.6.2 If/when `promise` is rejected,
// all respective `onRejected` callbacks must execute
// in the order of their originating calls to `then`.
invokeArrayFns(this.onRejectedCbs, reason)
}
}
}
}
看到这里你可能会有疑难,什么时候往 onFulfilledCbs
和 onRejectedCbs
里寄存对应的回调,答案是在调用 then
时:
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
// pending
if (this.status === STATUS_PENDING) {this.onFulfilledCbs.push(() => {setTimeout(() => {onFulfilled(this.value)
}, 0)
})
this.onRejectedCbs.push(() => {setTimeout(() => {onRejected(this.reason)
}, 0)
})
}
}
}
此时 Promise
处于 pending
状态,无奈确定其最初是 fulfilled
还是 rejected
,因而须要将回调函数寄存起来,待状态确定后再执行相应的回调函数。
注:invokeArrayFns
来源于 Vue.js 3 中的源码。
2.2.7 then
必须返回 Promise
promise2 = promise1.then(onFulfilled, onRejected)
那么,在咱们下面的代码中,then
怎么能力返回 Promise
呢?很简略:
class PromiseImpl {then(onFulfilled, onRejected) {let promise2 = new PromiseImpl((resolve, reject) => {if (this.status === STATUS_FULFILLED) {// ... 相干代码}
if (this.status === STATUS_REJECTED) {// ... 相干代码}
if (this.status === STATUS_PENDING) {// ... 相干代码}
})
return promise2
}
}
因为调用 then
之后返回一个新的 Promise
对象,使得咱们也能够进行链式调用:
Promise.resolve(42).then().then()...
2.2.7.1 ~ 2.2.7.4 这四点比拟重要,咱们上面别离来看看。
2.2.7.1 如果 onFulfilled
或 onRejected
返回一个值 x
,则运行 Promise
解决过程,[[Resolve]](promise2, x)
。
解释:其实所谓运行 Promise
解决过程就是执行某个操作,咱们把这个操作抽取成一个办法,并命名为:promiseResolutionProcedure(promise, x, resolve, reject)
。为了不便,咱们把 resolve
和 reject
一并透传进去。
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
let promise2 = new PromiseImpl((resolve, reject) => {if (this.status === STATUS_FULFILLED) {setTimeout(() => {
// 2.2.7.1
let x = onFulfilled(this.value)
promiseResolutionProcedure(promise2, x, resolve, reject)
}, 0)
}
if (this.status === STATUS_REJECTED) {setTimeout(() => {
// 2.2.7.1
let x = onRejected(this.reason)
promiseResolutionProcedure(promise2, x, resolve, reject)
}, 0)
}
if (this.status === STATUS_PENDING) {this.onFulfilledCbs.push(() => {setTimeout(() => {
// 2.2.7.1
let x = onFulfilled(this.value)
promiseResolutionProcedure(promise2, x, resolve, reject)
}, 0)
})
this.onRejectedCbs.push(() => {setTimeout(() => {
// 2.2.7.1
let x = onRejected(this.reason)
promiseResolutionProcedure(promise2, x, resolve, reject)
}, 0)
})
}
})
return promise2
}
}
2.2.7.2 如果 onFulfilled
或 onRejected
抛出一个异样 e
,则 promise2
必须 rejected
,并返回起因 e
。
解释:实现下面体现在执行 onFulfilled
或 onRejected
时应用 trycatch
包含起来,并在 catch
时调用 reject(e)
。
class PromiseImpl {then(onFulfilled, onRejected) {
// ... 其余代码
let promise2 = new PromiseImpl((resolve, reject) => {if (this.status === STATUS_FULFILLED) {setTimeout(() => {
try {
// 2.2.7.1
let x = onFulfilled(this.value)
promiseResolutionProcedure(promise2, x, resolve, reject)
} catch (e) {
// 2.2.7.2
reject(e)
}
}, 0)
}
if (this.status === STATUS_REJECTED) {setTimeout(() => {
try {
// 2.2.7.1
let x = onRejected(this.reason)
promiseResolutionProcedure(promise2, x, resolve, reject)
} catch (e) {
// 2.2.7.2
reject(e)
}
}, 0)
}
if (this.status === STATUS_PENDING) {this.onFulfilledCbs.push(() => {setTimeout(() => {
try {
// 2.2.7.1
let x = onFulfilled(this.value)
promiseResolutionProcedure(promise2, x, resolve, reject)
} catch (e) {
// 2.2.7.2
reject(e)
}
}, 0)
})
this.onRejectedCbs.push(() => {setTimeout(() => {
try {
// 2.2.7.1
let x = onRejected(this.reason)
promiseResolutionProcedure(promise2, x, resolve, reject)
} catch (e) {
// 2.2.7.2
reject(e)
}
}, 0)
})
}
})
// 2.2.7 `then` must return a promise
return promise2
}
}
2.2.7.3 如果 onFulfilled
不是函数且 promise1
曾经 fulfilled
,则 promise2
必须 fulfilled
且返回与 promise1
雷同的值。
解释:值的透传,例如:
Promise.resolve(42).then().then(value => console.log(value)) // 42
在第一个 then
时,咱们疏忽了 onFulfilled
,那么在链式调用的时候,须要把值透传给前面的 then
:
class PromiseImpl {then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// ... 其余代码
}
}
2.2.7.4 如果 onRejected
不是函数且 promise1
曾经 rejected
,则 promise2
必须 rejected
且返回与 promise1
雷同的起因。
解释:同理,起因也要透传:
Promise.reject('reason').catch().catch(reason => console.log(reason)) // 'reason'
class PromiseImpl {then(onFulfilled, onRejected) {onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason}
// ... 其余代码
}
}
2.3 Promise 解决过程
Promise
解决过程是一个形象的操作,输出一个 promise
和一个值,这里咱们将其命名为 promiseResolutionProcedure(promise, x, resolve, reject)
,并在调用时传入 resolve
和 reject
两个办法,别离用于在 fulfilled
和 rejected
时调用。
2.3.1 如果 promise
和 x
为同一个对象
如果 promise
和 x
为同一个对象,回绝该 promise
,起因为 TypeError
。
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// 2.3.1 If `promise` and `x` refer to the same object,
// reject `promise` with a `TypeError` as the reason
if (promise === x) {return reject(new TypeError('`promise` and `x` refer to the same object, see: https://promisesaplus.com/#point-48'))
}
// ... 其余代码
}
2.3.2 如果 x
是一个 Promise 对象
如果 x
是一个 Promise
对象,则须要递归执行:
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
// 2.3.2 If `x` is a promise, adopt its state:
// 2.3.2.1 If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.
// 2.3.2.2 If/when `x` is fulfilled, fulfill `promise` with the same value.
// 2.3.2.3 If/when `x` is rejected, reject `promise` with the same reason.
if (x instanceof PromiseImpl) {
return x.then(value => promiseResolutionProcedure(promise, value, resolve, reject),
reason => reject(reason)
)
}
}
2.3.3 如果 x
是一个对象或函数
如果 x
是一个对象或函数:
2.3.3.1 将 x.then
赋值给 x
解释:这样做有两个目标:
- 防止对
x.then
的屡次拜访(这也是日常开发中的一个小技巧,当要屡次拜访一个对象的同一属性时,通常咱们会应用一个变量将该属性存储起来,防止屡次进行原型链查找) - 执行过程中
x.then
的值可能被扭转
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
// 2.3.3 Otherwise, if x is an object or function
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
// 2.3.3.1 Let `then` be `x.then`
let then = x.then
}
}
2.3.3.2 如果在对 x.then
取值时抛出异样 e
,则回绝该 promise
,起因为 e
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {// 2.3.3.1 Let `then` be `x.then`} catch (e) {
// 2.3.3.2 If retrieving the property `x.then` results in a thrown exception `e`,
// reject `promise` with `e` as the reason.
reject(e)
}
}
}
2.3.3.3 如果 then
是函数
如果 then
是函数,则将 x
作为 then
的作用域,并调用 then
,同时传入两个回调函数,第一个名为 resolvePromise
,第二个名为 rejectPromise
。
解释:意思就是在调用 then
的时候要指定其 this
值为 x
,同时须要传入两个回调函数。这时候用 call()
来实现是最好不过了:
then.call(x, resolvePromise, rejectPromise)
2.3.3.3.1 ~ 2.3.3.3.4 总结起来的意思如下:
-
2.3.3.3.1 如果
resolvePromise
被调用,则递归调用promiseResolutionProcedure
,值为y
。因为Promise
中能够嵌套Promise
:then.call( x, y => promiseResolutionProcedure(promise, y, resolve, reject), rejectPromise )
-
2.3.3.3.2 如果
rejectPromise
被调用,参数为r
,则拒绝执行Promise
,起因为r
:then.call( x, y => promiseResolutionProcedure(promise, y, resolve, reject), // resolvePromise r => reject(r) // rejectPromise )
-
2.3.3.3.3 如果
resolvePromise
和rejectPromise
均被调用,或者被同一参数调用了屡次,则只执行首次调用解释:这里咱们能够通过设置一个标记位来解决,而后别离在
resolvePromise
和rejectPromise
这两个回调函数内做判断:// 初始化时设置为 false let called = false if (called) {return} // `resolvePromise` 和 `rejectPromise` 被调用时设置为 true called = true
- 2.3.3.3.4 调用
then
抛出异样e
时,如果这时resolvePromise
或rejectPromise
曾经被调用,则疏忽;否则回绝该Promise
,起因为e
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
let called = false
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
// 2.3.3.3 If `then` is a function, call it with `x` as `this`,
// first argument `resolvePromise`, and second argument `rejectPromise`
then.call(
// call it with `x` as `this`
x,
// `resolvePromise`
// 2.3.3.3.1 If/when `resolvePromise` is called with a value `y`,
// run `[[Resolve]](promise, y)`.
y => {
// 2.3.3.3.3 If both `resolvePromise` and `rejectPromise` are called,
// or multiple calls to the same argument are made,
// the first call takes precedence, and any further calls are ignored.
if (called) {return}
called = true
promiseResolutionProcedure(promise, y, resolve, reject)
},
// `rejectPromise`
// 2.3.3.3.2 If/when `rejectPromise` is called with a reason `r`,
// reject `promise` with `r`
r => {
// 2.3.3.3.3
if (called) {return}
called = true
reject(r)
}
)
} else {
// 2.3.3.4 If `then` is not a function, fulfill `promise` with `x`
resolve(x)
}
} catch (e) {
// 2.3.3.3.3
if (called) {return}
called = true
// 2.3.3.2 If retrieving the property `x.then` results in a thrown exception `e`,
// reject `promise` with `e` as the reason.
// 2.3.3.3.4 If calling `then` throws an exception `e`
// 2.3.3.3.4.1 If `resolvePromise` or `rejectPromise` have been called, ignore it
// 2.3.3.3.4.2 Otherwise, reject `promise` with `e` as the reason
reject(e)
}
}
}
2.3.3.4 如果 then
不是函数,则执行该 promise
,参数为 x
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {// 2.3.3.3} else {
// 2.3.3.4 If `then` is not a function, fulfill `promise` with `x`
resolve(x)
}
} catch (e) {}}
}
2.3.4 如果 x
既不是对象也不是函数
如果 x
既不是对象也不是函数,执行该 promise
,参数为 x
:
const promiseResolutionProcedure = (promise, x, resolve, reject) => {
// ... 其余代码
if ((x !== null && typeof x === 'object') || typeof x === 'function') {// 2.3.3} else {
// 2.3.4 If `x` is not an object or function, fulfill `promise` with `x`
resolve(x)
}
}
至此,咱们的自定义 Promise
曾经实现。这是源码:promise-aplus-implementing。
4. 如何测试
Promise/A+ 标准提供了一个测试脚本:promises-tests,你能够用它来测试你的实现是否符合规范。
在 PromiseImpl.js
里增加以下代码:
// PromiseImpl.js
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
const invokeArrayFns = (fns, arg) => {// ... 相干代码}
const promiseResolutionProcedure = (promise, x, resolve, reject) => {// ... 相干代码}
class PromiseImpl {// ... 相干代码}
PromiseImpl.defer = PromiseImpl.deferred = () => {const dfd = {}
dfd.promise = new PromiseImpl((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = PromiseImpl
而后在 package.json
里增加 scripts
:
// package.json
{
"scripts": {"test": "promises-aplus-tests PromiseImpl.js"}
}
而后,运行 npm run test
,执行测试脚本,如果你的实现合乎 Promise/A+ 标准的话,所有测试用例均会通过。
5. 其余 API 的实现
Promise/A+ 标准只规定了 then()
办法的实现,其余的如 catch()
、finally()
等办法并不在标准里。但就实现而言,这些办法能够通过对 then()
办法或其余形式进行二次封装来实现。
另外,像是 all()
、race()
等这些办法,其参数为一个 可迭代 对象,如 Array
、Set
、Map
等。那么,什么是 可迭代 对象依据 ES6 中的标准,要成为 可迭代 对象,一个对象必须实现 @@iterator
办法,即该对象必须有一个名为 @@iterator
的属性,通常咱们应用常量 Symbol.iterator 来拜访该属性。
依据以上信息,判断一个参数是否为 可迭代 对象,其实现如下:
const isIterable = value => !!value && typeof value[Symbol.iterator] === 'function'
Promise.resolve
Promise.resolve(value)
,静态方法,其参数有以下几种可能:
- 参数是
Promise
对象 - 参数是
thenable
对象(领有then()
办法的对象) - 参数是原始值或不具备
then()
办法的对象 - 参数为空
因而,其返回值由其参数决定:有可能是一个具体的值,也有可能是一个 Promise
对象:
class PromiseImpl {static resolve(value) {return new PromiseImpl((resolve, reject) => resolve(value))
}
}
Promise.reject
Promise.reject(reason)
,静态方法,参数为 Promise
拒绝执行时的起因,同时返回一个 Promise
对象,状态为 rejected
:
class PromiseImpl {static reject(reason) {return new PromiseImpl((resolve, reject) => reject(reason))
}
}
Promise.prototype.catch
Promise.prototype.catch(onRejected)
其实就是 then(null, onRejected)
的语法糖:
class PromiseImpl {catch(onRejected) {return this.then(null, onRejected)
}
}
Promise.prototype.finally
顾名思义,不论 Promise
最初的后果是 fulfilled
还是 rejected
,finally
里的语句都会执行:
class PromiseImpl {finally(onFinally) {
return this.then(value => PromiseImpl.resolve(onFinally()).then(() => value),
reason => PromiseImpl.resolve(onFinally()).then(() => { throw reason})
)
}
}
Promise.all
Promise.all(iterable)
,静态方法 ,参数为 可迭代 对象:
- 只有当
iterable
里所有的Promise
都胜利执行后才会fulfilled
,回调函数的返回值为所有Promise
的返回值组成的数组,程序与iterable
的程序保持一致。 - 一旦有一个
Promise
拒绝执行,则状态为rejected
,并且将第一个拒绝执行的Promise
的起因作为回调函数的返回值。 - 该办法会返回一个
Promise
。
class PromiseImpl {static all(iterable) {if (!isIterable(iterable)) {return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
return new PromiseImpl((resolve, reject) => {
// `fulfilled` 的 Promise 数量
let fulfilledCount = 0
// 收集 Promise `fulfilled` 时的值
const res = []
for (let i = 0; i < iterable.length; i++) {const iterator = iterable[i]
iterator.then(
value => {res[i] = value
fulfilledCount++
if (fulfilledCount === iterable.length) {resolve(res)
}
},
reason => reject(reason)
)
}
})
}
}
测试一下:
const promise1 = Promise.resolve(42)
const promise2 = new PromiseImpl((resolve, reject) => setTimeout(() => resolve('value2'), 1000))
PromiseImpl.all([
promise1,
promise2
]).then(values => console.log('values:', values))
后果:
values: [42, 'value2']
如同挺完满的,然而事实果真如此吗?认真看看咱们的代码,如果 iterable
是一个空的数组呢?如果 iterable
里有不是 Promise
的呢?就像这样:
PromiseImpl.all([])
PromiseImpl.all([promise1, promise2, 'value3'])
这种状况下执行后面的代码,是得不到任何后果的。因而,代码还要再改良一下:
class PromiseImpl {static all(iterable) {if (!isIterable(iterable)) {return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
return new PromiseImpl((resolve, reject) => {
// `fulfilled` 的 Promise 数量
let fulfilledCount = 0
// 收集 Promise `fulfilled` 时的值
const res = []
// - 填充 `res` 的值
// - 减少 `fulfilledCount`
// - 判断所有 `Promise` 是否曾经全副胜利执行
const processRes = (index, value) => {res[index] = value
fulfilledCount++
if (fulfilledCount === iterable.length) {resolve(res)
}
}
if (iterable.length === 0) {resolve(res)
} else {for (let i = 0; i < iterable.length; i++) {const iterator = iterable[i]
if (iterator && typeof iterator.then === 'function') {
iterator.then(value => processRes(i, value),
reason => reject(reason)
)
} else {processRes(i, iterator)
}
}
}
})
}
}
当初再来测试一下:
const promise1 = PromiseImpl.resolve(42)
const promise2 = 3
const promise3 = new PromiseImpl((resolve, reject) => setTimeout(() => resolve('value3'), 1000))
PromiseImpl.all([
promise1,
promise2,
promise3,
'a'
]).then(values => console.log('values:', values))
// 后果:values: [42, 3, 'value3', 'a']
PromiseImpl.all([]).then(values => console.log('values:', values))
// 后果:values: []
Promise.allSettled
Promise.allSettled(iterable)
,静态方法 ,参数为 可迭代 对象:
- 当
iterable
里所有的Promise
都胜利执行或拒绝执行后才实现,返回值是一个对象数组。 - 如果有嵌套
Promise
,须要期待该Promise
实现。 - 返回一个新的
Promise
对象。
对于 allSettled
,咱们能够在 all
的根底上进行封装:
class PromiseImpl {static allSettled(iterable) {if (!isIterable(iterable)) {return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
const promises = iterable.map(iterator => PromiseImpl.resolve(iterator).then(value => ({ status: STATUS_FULFILLED, value}),
reason => ({status: STATUS_REJECTED, reason})
))
return PromiseImpl.all(promises)
}
}
测试后果:
PromiseImpl.allSettled([PromiseImpl.resolve(42),
PromiseImpl.reject('Oops!'),
PromiseImpl.resolve(PromiseImpl.resolve(4242)),
'a'
]).then(values => console.log(values))
// 后果:// [// { status: 'fulfilled', value: 42},
// {status: 'rejected', reason: 'Oops!'},
// {status: 'fulfilled', value: 4242},
// {status: 'fulfilled', value: 'a'}
// ]
Promise.race
Promise.race(iterable)
,静态方法 ,参数为 可迭代 对象:
- 当
iterable
里的任一Promise
胜利执行或拒绝执行时,应用该Promise
胜利返回的值或拒绝执行的起因 - 如果
iterable
为空,则处于pending
状态 - 返回一个新的
Promise
对象
例如:
Promise.race([Promise.resolve(42),
Promise.reject('Oops!'),
'a'
]).then(values => console.log(values))
.catch(reason => console.log(reason))
// 后果:42
Promise.race([Promise.reject('Oops!'),
Promise.resolve(42),
'a'
]).then(values => console.log(values))
.catch(reason => console.log(reason))
// 后果:Oops!
实现如下:
class PromiseImpl {static race(iterable) {if (!isIterable(iterable)) {return new TypeError(`TypeError: ${typeof iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
return new PromiseImpl((resolve, reject) => {if (iterable.length === 0) {return} else {for (let i = 0; i < iterable.length; i++) {const iterator = iterable[i]
if (iterator && typeof iterator.then === 'function') {
iterator.then(value => resolve(value),
reason => reject(reason)
)
return
} else {resolve(iterator)
return
}
}
}
})
}
}
这里要留神一点:别忘了应用 return
来完结 for
循环。
测试后果:
PromiseImpl.race([PromiseImpl.resolve(42),
PromiseImpl.reject('Oops!'),
'a'
]).then(values => console.log(values))
.catch(reason => console.log(reason))
// 后果:42
PromiseImpl.race([PromiseImpl.reject('Oops!'),
PromiseImpl.resolve(42),
'a'
]).then(values => console.log(values))
.catch(reason => console.log(reason))
// 后果:'Oops!'
PromiseImpl.race([
'a',
PromiseImpl.reject('Oops!'),
PromiseImpl.resolve(42)
]).then(values => console.log(values))
.catch(reason => console.log(reason))
// 后果:'a'
6. 独特探讨
- 在 2.3.3.1 把
x.then
赋值给then
中,什么状况下x.then
的指向会被扭转? - 在 2.3.3.3 如果
then
是函数 中,除了应用call()
之外,还有什么其余形式实现吗?
7. 总结
实现 Promise
,根本分为三个步骤:
- 定义
Promise
的状态 - 实现
then
办法 - 实现
Promise
解决过程
8. 写在最初
以前,我在意能不能自己实现一个 Promise
,到处找文章,这块代码 Ctrl+C
,那块代码 Ctrl+V
。当初,我看重的是实现的过程,在这个过程中,你不仅会对 Promise
更加相熟,还能够学习如何将标准一步步转为理论代码。做对的事,远比把事件做对重要。
如果你感觉这篇文章对你有帮忙,还请:点赞、珍藏、转发 ;如果你有疑难,请在评论区写进去,咱们一起探讨。同时也欢送关注我的公众号: 前端笔记。
参考资料
- Promises/A+
- Promise A+ 标准
- 应用 Promise – MDN
- Promise – MDN