本文已整顿到 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 的 fulfill
function fulfill(promise, value) {
promise.state = FULFILLED
promise.value = value
}
// 带 reason 的 reject
function 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,则调用 handler
function 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 中存储的所有 handlers
function 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 = null
const 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) // 1
APromise.race([sleep(300), sleep(100), sleep(200)]).then(console.log) // 100
APromise.race([sleep(3000), err(100), sleep(2000)]).catch(console.error) // 100
APromise.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) // 100
APromise.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)) // 1
APromise.any([sleep(3000), err(100), sleep(2000)]).then(console.info) // 2000
APromise.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
- 性能晋升优化技巧
- 对于异步堆栈跟踪