本文已整顿到 Github,地址 blog。
实现 Promise/A+
本文的指标是编写一个与 then/promise
相似的合乎 Promise/A+ 的实现。
以下前半部分译自 Implementing promises from scratch,也是本文的重点。你能够查看原文,它还应用 TDD 形式,编写一些测试用例,帮忙你了解。下半局部是 Promise 各个办法的实现。
Promise 状态
Promise 是必须处于以下状态之一的对象/函数:PENDING
、FULFILLED
和 REJECTED
,最后 promise 处于 PENDING
状态。
Promise 能够从 PENDING
状态转换为带 value
值的 FULFILLED
状态或带 reason
的 REJECTED
状态。
为了进行状态转换,promise 构造函数接管到一个名为 executor
的函数,executor
会立刻被调用,调用时应用两个函数 fulfill
和 reject
来执行状态转换:
fulfill(value)
— 通过value
从PENDING
到FULFILLED
,value
当初是 promise 的属性。reject(reason)
— 通过reason
从PENDING
到REJECTED
,reason
当初是 promise 的属性。
最后的实现很简略:
// 可能的状态const PENDING = 'PENDING'const FULFILLED = 'FULFILLED'const REJECTED = 'REJECTED'class APromise { constructor(executor) { // 初始化状态 this.state = PENDING // 胜利的 value 或回绝的 reason 在外部映射为 value,最后 promise 没有值 // 调用立刻执行程序 doResolve(this, executor) }}// 带 value 的 fulfillfunction fulfill(promise, value) { promise.state = FULFILLED promise.value = value}// 带 reason 的 rejectfunction reject(promise, reason) { promise.state = REJECTED promise.value = reason}// 创立作为 executor 参数的 fulfill/reject 函数function doResolve(promise, executor) { function wrapFulfill(value) { fulfill(promise, value) } function wrapReject(reason) { reject(promise, reason) } executor(wrapFulfill, wrapReject)}
察看状态变动
为了察看 promise 状态的变动(以及胜利的值或回绝的起因),咱们应用 then
办法,该办法接管两个参数,一个 onFulfilled
函数和一个 onRejected
函数,调用这些函数的规定如下:
- 当 promise 处于
FULFILLED
状态时,onFulfilled
函数将被调用,并带有 promise 履行的value
,例如onFulfilled(value)
- 当 promise 处于
REJECTED
状态时,onRejected
函数将被调用,并带有 promise 被回绝的reason
,例如onRejected(reason)
咱们将这些函数称为 promise handlers
(译为处理程序)。
让咱们将 then
函数增加到类原型中,留神它会依据 Promise 的状态调用 onFulfilled
或 onRejected
函数。
class APromise { // ... then(onFulfilled, onRejected) { handleResolved(this, onFulfilled, onRejected) } // ...}function handleResolved(promise, onFulfilled, onRejected) { const cb = promise.state === FULFILLED ? onFulfilled : onRejected cb(promise.value)}
单向转换
一旦转换到其中一个 FULFILLED
或 REJECTED
状态,promise 不得转换到任何其余状态。
在咱们以后的实现中,调用 executor
的函数应该确保只调用一次 fulfill
或 reject
,后续调用应该被疏忽
function doResolve(promise, executor) { let called = false function wrapFulfill(value) { if (called) return called = true fulfill(promise, value) } function wrapReject(reason) { if (called) return called = true reject(promise, reason) } executor(wrapFulfill, wrapReject)}
解决 executor 谬误
如果执行 executor
失败,promise 应转换到 REJECTED
状态,并阐明失败起因
function doResolve(promise, executor) { // ... try { executor(wrapFulfill, wrapReject) } catch (err) { wrapReject(err) }}
异步 executor
如果解析器的 fulfill/reject
是异步执行,则咱们的 .then
办法将失败,因为它的 handlers 将立刻执行。
让咱们向 Promise 增加一个队列,它的目标是存储一旦 Promise 状态从 PENDING
更改为其余状态时将调用的 handlers,同时咱们的 .then
办法应该查看 Promise 状态,以决定是立刻调用 handler 还是存储 handler,让咱们将此逻辑挪动到新的辅助函数 handle
。
class APromise { constructor(executor) { this.state = PENDING // 存储 .then handler 队列 this.queue = [] doResolve(this, executor) } then(onFulfilled, onRejected) { handle(this, { onFulfilled, onRejected }) }}// 查看 promise 的状态:// - 如果 promise 为 PENDING,将其推入 queue 以供当前应用// - 如果 promise 还不是 PENDING,则调用 handlerfunction handle(promise, handler) { if (promise.state === PENDING) { // 如果为 PENDING,推入 queue promise.queue.push(handler) } else { // 立刻执行 handleResolved(promise, handler) }}function handleResolved(promise, handler) { const cb = promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected cb(promise.value)}
此外,应该更新 fulfill
和 reject
办法,以便在调用时调用 Promise 中存储的所有 handlers,这将在更新状态和值后调用的新函数 finale
中实现。
function fulfill(promise, value) // ... finale(promise)}function reject(promise, reason) { // ... finale(promise)}// 调用 promise 中存储的所有 handlersfunction finale(promise) { const length = promise.queue.length for (let i = 0; i < length; i += 1) { handle(promise, promise.queue[i]) }}
链式的 Promise
咱们的 .then
办法应该返回一个新的 Promise。
实现也很简略,然而咱们将看到新的 Promise 以不同于应用 executor
的形式转换到不同的状态,新的 Promise 应用 handlers 进行转换,如下所示:
如果
onFulfilled
或onRejected
函数被调用- 如果执行时没有谬误,Promise 将转换为
FULFILLED
状态,返回值作为value
- 如果执行时呈现谬误,Promise 将转换到
REJECTED
状态,并将谬误作为reason
- 如果执行时没有谬误,Promise 将转换为
让咱们做一个 .then
办法首先返回 Promise:
class APromise { // ... then(onFulfilled, onRejected) { // 空的 executor const promise = new APromise(() => {}) handle(this, { onFulfilled, onRejected }) return promise }}
对于实现,咱们首先必须将新的 Promise 也存储在 handler 队列中,这样,如果察看到的 Promise 被解析,那么队列中的元素就晓得须要解析哪个 Promise。
class APromise { // ... then(onFulfilled, onRejected) { const promise = new APromise(() => {}) // 同时保留 promise handle(this, { promise, onFulfilled, onRejected }) return promise }}function handleResolved(promise, handler) { const cb = promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected // 执行 handler 并依据规定进行转换 try { const value = cb(promise.value) fulfill(handler.promise, value) } catch (err) { reject(handler.promise, err) }}
异步 handlers
接下来,让咱们思考 handler 返回 Promise 的状况,在这种状况下,作为 handler 一部分的 Promise(不是返回的 Promise)应该采纳返回 Promise 的状态履行值或回绝起因。
让咱们构想以下场景:
const executor = fulfill => setTimeout(fulfill, 0, 'p')const p = new APromise(executor)const qOnFulfilled = value => new APromise(fulfill => fulfill(value + 'q'))const q = p.then(qOnFulfilled)const rOnFulfilled = value => ( // 值应为 pq)const r = q.then(rOnFulfilled)
在咱们以后的实现中,元组 { q, qOnFulfilled }
存储在 p
的 handlers 中,并且咱们确信在 q
存储元组 { r, rOnFulfilled }
之前,qOnFulfilled
被调用,咱们能够利用这一事实,并检测 handler 何时返回一个 Promise,在返回的 Promise 中存储观察者,例如在 qOnFulfilled
返回的 Promise 上存储 { r, onFulfilled }
。
请留神,咱们应用的是 while
,因为嵌套的 Promise 自身可能有另一个 Promise 作为解析值。
function handle(promise, handler) { // 取最深处的 promise 的状态 while (promise.value instanceof APromise) { promise = promise.value } // ...}
其余状况
有效的 handlers
如果本来应该是函数的 handler 不是函数,那么咱们的实现就会失败:
const p = new APromise((fulfill) => fulfill('p'))const qOnFulfilled = nullconst q = p.then(qOnFulfilled)
在这种状况下,q
应该立刻用 p
的值进行解析
function handleResolved(promise, handler) { const cb = promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected // 如果 handler 不是函数,则立刻解析 if (typeof cb !== 'function') { if (promise.state === FULFILLED) { fulfill(handler.promise, promise.value) } else { reject(handler.promise, promise.value) } return } // ...}
在事件循环之后执行 handlers
要求 2.2.4,正如 3.1 中指出的,handlers 被一个新的堆栈调用,此外,即便 executor/handlers
是同步的,也能够确保未来调用观察者,从而使 Promise 解析保持一致。
咱们能够应用任何容许咱们在事件循环之后调用函数的函数,这包含 setTimeout
、setImmediate
和 requestAnimationFrame
function handleResolved(promise, handler) { setImmediate(() => { // ... })}
以已解决的 Promise 为理由回绝
要求 2.2.7.2,只有当 promise 不处于 REJECTED
状态时才采纳嵌套 promise 的状态。
function handle(promise, handler) { // 以返回的 promise 的状态为例 while (promise.state !== REJECTED && promise.value instanceof APromise) {} // ...}
Promise 自身无奈解决
要求 2.3.1,在 fulfill
办法上,让咱们查看履行值是否等于 Promise 自身,如果是这样,则抛出一个 TypeError
:
function fulfill(promise, value) { if (value === promise) { return reject( promise, new TypeError('A promise cannot be resolved with itself.') ) } // ...}
Thenable
2.3.3.3 相干要求,handler 的返回值可能是一个 thenable
,一个 object/function,它具备一个可拜访的 then
属性,这是一个函数,then
函数就像一个 executor,它接管一个 fulfill
和 reject
回调,应该用来转换 thenable 的状态。
让咱们批改 fulfill
办法并增加对 thenable 的查看,留神拜访属性并不总是平安的操作(例如,属性可能应用 getter
),这就是为什么咱们应该将它包装在 try/catch
中。
另外,thenable 的 then
应该被调用为 this
function fulfill(promise, value) { if (value === promise) { return reject( promise, new TypeError('A promise cannot be resolved with itself.') ) } if (value && (typeof value === 'object' || typeof value === 'function')) { let then try { then = value.then } catch (err) { return reject(promise, err) } // promise if (then === promise.then && promise instanceof APromise) { promise.state = FULFILLED promise.value = value return finale(promise) } // thenable if (typeof then === 'function') { return doResolve(promise, then.bind(value)) } } // primitive promise.state = FULFILLED promise.value = value finale(promise)}
实现 Promise.catch()
class APromise { // ... catch(onRejected) { return this.then(null, onRejected) }}
实现 Promise.finally()
Promise.prototype.finally()
返回一个Promise
。在 promise 完结时,无论后果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否胜利实现后都须要执行的代码提供了一种形式。
将返回 promise 的后果在 then
一次,无论返回的是 fulfilled 还是 rejected,都执行给定的回调函数。
class APromise { finally(onFinally) { return this.then( /* onFulfilled */ (res) => Promise.resolve(onFinally().call(this)).then(() => res), /* onRejected */ (err) => Promise.resolve(onFinally().call(this)).then(() => { throw err }) ) }}
示例:
new APromise((resolve, reject) => { resolve('ok')}) .then((res) => { console.log(res) // ok }) .finally(() => { console.log('finally') // finally })
实现 Promise.resolve()
外部返回一个 promise,调用 resolve
办法,将 value
参数传入
class APromise { // ... static resolve(value) { return new APromise((resolve) => resolve(value)) }}
实现 Promise.reject()
外部返回了一个 promise,调用 reject
办法,将 reason
参数传入
class APromise { // ... static reject(reason) { return A((resolve, reject) => reject(reason)) }}
实现 Promise.race()
Promise.race
与 Promise.all
相似,但只期待第一个 settled 的 promise 并获得其 result/error
,第一个 settled promise 之后,所有其余的 result/error
都会被疏忽。
class APromise { // ... static race(arr) { const _Promise = this if (!Array.isArray(arr)) { return _Promise.reject(new TypeError('race() only accepts an array')) } return new _Promise((resolve, reject) => { arr.forEach((p) => { _Promise.resolve(p).then(resolve, reject) }) }) }}
示例:
const sleep = (sm) => new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.race([1, 2, 3]).then(console.log) // 1APromise.race([sleep(300), sleep(100), sleep(200)]).then(console.log) // 100APromise.race([sleep(3000), err(100), sleep(2000)]).catch(console.error) // 100APromise.race([err(50), err(60)]).catch(console.error) // 50
实现 Promise.all()
- 如果传入的可迭代对象内的 promise 全副胜利,那么就返回
resolve
胜利的数组 - 一旦有一个 promise 执行失败,
Promise.all
间接返回谬误的那个reject
。
class APromise { // ... static all(promises) { let remaining = promises.length // 判断是否为空 if (remaining === 0) return APromise.resolve([]) return new APromise((resolve, reject) => { promises.reduce((acc, promise, i) => { APromise.resolve(promise).then( (res) => { acc[i] = res --remaining || resolve(acc) }, (err) => { reject(err) } ) return acc }, []) }) }}
测试:
const sleep = (sm) => new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.all([1, 2, 3]).then(console.log) // [1, 2, 3]APromise.all([sleep(300), sleep(100), sleep(200)]).then(console.log)APromise.all([sleep(3000), err(100), sleep(2000)]).catch(console.error) // 100APromise.all([err(50), err(60)]).catch(console.error) // 50
实现 promise.any()
Promise.any()
接管一个Promise
可迭代对象,只有其中的一个promise
胜利,就返回那个曾经胜利的promise
。如果可迭代对象中没有一个promise
胜利(即所有的promises
都失败/回绝),就返回一个失败的promise
和AggregateError
类型的实例。
Promise.any
的行为跟 Promise.all
刚好相同:
- 只有有一个胜利,那么就立刻
resolve
进来 - 如果全副失败,那么就将所有
reject
后果收集起来并返回AggregateError
class APromise { // ... static any(promises) { return A((resolve, reject) => { if (promises.length === 0) return reject(new AggregateError('All promises were rejected')) promises.reduce((acc, cur) => { Promise.resolve(cur).then( (data) => { resolve(data) }, (err) => { acc.push(err) if (acc.length === promises.length) reject(new AggregateError('All promises were rejected')) } ) return acc }, []) }) }}
示例:
const sleep = (sm) => new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.any([1, 2, 3]).then((o) => console.log(o)) // 1APromise.any([sleep(3000), err(100), sleep(2000)]).then(console.info) // 2000APromise.any([err(50), err(60)]).catch(console.log) // AggregateError
实现 Promise.allSettled()
Promise.allSettled()
办法返回一个在所有给定的 promise 都曾经 fulfilled 或 rejected 后的 promise,并带有一个对象数组,每个对象示意对应的 promise 后果。
class APromise { // ... static allSettled(values) { let promises = [].slice.call(values) return new APromise((resolve, reject) => { let result = [], count = 0 promises.forEach((promise) => { APromise.resolve(promise) .then((value) => { result.push({ status: 'fulfilled', value }) }) .catch((err) => { result.push({ status: 'rejected', value: err }) }) .finally(() => { if (++count === promises.length) { resolve(result) } }) }) }) }}
示例:
const sleep = (sm) => new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.allSettled([1, 2, 3]).then(console.log)APromise.allSettled([sleep(300), sleep(100), sleep(200)]).then(console.log)APromise.allSettled([sleep(3000), err(100), sleep(2000)]).catch(console.error)APromise.allSettled([err(50), err(60)]).then(console.log)
更多材料
以上 Promise/A+ 残缺示例。
- JavaScript Promise 迷你书(中文版)
- Promises/A+
- Promises/A 与 Promises/A+ 的区别
- Conformant Implementations
- 通过从头开始构建 Promise 来学习 JavaScript Promise
- implementing
- Optimization killers
- es6-promise
- 性能晋升优化技巧
- 对于异步堆栈跟踪