完整实现Promise

avatar

先前实现过 最简实现 Promise 支持链式调用,这次我们来实现完整的 Promise

前提

在熟悉 Promise 的前提下,我们知道 Promise 底层内部维护了三种状态:

PENDING 代表异步状态待定

REJECTED 代表异步状态拒绝

FULFILLED 代表异步状态解决

以及内部维护了

外部传入对应 resolve 回调数组resolveCallbacks

外部传入对应 reject 回调数组rejectCallbacks

以及最重要的三个 then, resolve, reject 方法
先实现最简单的 resolve reject :如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'

class Promise {
constructor(execute) {
this.state = PENDING
this.value = undefined
this.reason = undefined
this.resolveCallbacks = []
this.rejectCallbacks = []
try {
execute(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}

resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
while (this.resolveCallbacks.length > 0) {
this.resolveCallbacks.shift().call(undefined)
}
}
}

reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
while (this.rejectCallbacks.length > 0) {
this.rejectCallbacks.shift().call(undefined)
}
}
}
}

Promise.prototype.then()

先上 then 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const p2 = new Promise((resolve, reject) => {
if (this.state === REJECTED) {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.state === FULFILLED) {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.state === PENDING) {
this.resolveCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.rejectCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return p2
}

这里的 queueMicrotask 是原生 api,为了模拟浏览器下微任务执行。而 resolvePromise 是为了抽离可复用逻辑,目的做 promise 的一些边际处理及逻辑判断。
按照 Promise/A+规范,需要在 then 调用的时候,返回新的一个 Promise,并在内部判断 state 的状态,做对应处理。

  1. 判断 then 方法的两个参数 onRejected 和 onFulfilled 是否为 function。
    1.1 onRejected 和 onFulfilled 都是 function,继续执行下一步。
    1.2 onRejected 不是 function,将 onRejected 赋值为箭头函数,参数为 reason 执行 throw reason
    1.3 onFulfilled 不是 function,将 onFulfilled 赋值为箭头函数,参数为 value 执行 return value
  2. 当前 Promise 状态为 rejected:
    2.1 onRejected 方法传入 this.reason 参数,异步执行。
    2.2 对执行的 onRejected 方法做容错处理,catch 错误作为 reject 方法参数执行。 3.当前 Promise 状态为 fulfilled:
    3.1 onFulfilled 方法传入 this.value 参数,异步执行。
    3.2 对执行的 onFulfilled 方法做容错处理,catch 错误作为 reject 方法参数执行。
  3. 当前 Promise 状态为 pending:
    4.1 收集 onFulfilled 和 onRejected 两个回调函数分别 push 给 resolveCallbacks 和 rejectCallbacks。
    4.2 收集的回调函数同样如上所述,先做异步执行再做容错处理。
  4. 返回一个 Promise 实例对象。

Promise 解决程序(resolvePromise 方法)

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function resolvePromise(p2, x, resolve, reject) {
if (x === p2) {
// 2.3.1 如果promise和x引用同一个对象
reject(new TypeError())
} else if ((x !== null && typeof x === 'object') || typeof x === 'function') {
// 2.3.2 如果x是一个promise
// 2.3.3 如果x是一个对象或函数
let called
try {
let then = x.then
if (typeof then === 'function') {
then.call(x,
(y) => {
if (called) { return }
called = true
resolvePromise(p2, y, resolve, reject)
},
(r) => {
if (called) { return }
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) { return }
called = true
reject(e)
}
} else {
resolve(x)
}
}
  1. 如果 x 和 promise 引用同一个对象:
    1.1 调用 reject 方法,其参数为 new TypeError()
  2. 如果 x 是一个 promise 或 x 是一个对象或函数:
    2.1 定义一个 called 变量用于记录 then.call 参数中两个回调函数的调用情况。
    2.2 定义一个 then 变量等于 x.then
    2.3 then 是一个函数。使用 call 方法绑定 x 对象,传入解决回调函数和拒绝回调函数作为参数。同时利用 called 变量记录 then.call 参数中两个回调函数的调用情况。
    2.4 then 不是函数。调用 resolve 方法解决 Promise,其参数为 x
    2.5 对以上 2.2 检索属性和 2.3 调用方法的操作放在一起做容错处理。catch 错误作为 reject 方法参数执行。同样利用 called 变量记录 then.call 参数中两个回调函数的调用情况。
  3. 如果 x 都没有出现以上两种状况:
    调用 resolve 方法解决 Promise,其参数为 x
  4. called 变量的作用:根据 Promise A+ 2.3.3.3.3 规范:两个参数作为函数第一次调用优先,以后的调用都会被忽略。

因此我们在以上两个回调函数中这样处理:

已经调用过一次:此时 called 已经为 true,直接 return 忽略
首次调用:此时 called 为 undefined,调用后 called 设为 true
注意:2.3 中的 catch 可能会发生(两个回调函数)已经调用但出现错误的情况,因此同样按上述说明处理。

Promise.all()

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static all(promises) {
return new Promise((resolve, reject) => {
let result = []
let countPromise = 0
const finishProgram = (index, val) => {
result[index] = val
if (++countPromise === promises.length) {
resolve(result)
}
}
for (let index = 0; index < promises.length; index++) {
let p = promises[index]
if (p && typeof p.then === 'function') {
p.then(value => {
finishProgram(index, value)
}, reject)
} else {
finishProgram(index, p)
}
}
})
}

Promise.race()

代码如下:

1
2
3
4
5
6
7
8
9
10
11
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
if (p && typeof p.then === 'function') {
p.then(resolve, reject)
} else {
resolve(p)
}
})
})
}

Promise.allSettled()

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static allSettled(promises) {
return new Promise((resolve, reject) => {
let countPromise = 0
const result = []
const finishProgram = (index, val) => {
result[index] = val
if (++countPromise === promises.length) {
resolve(result)
}
}
for (let index = 0; index < promises.length; index++) {
const p = promises[index]
if (p && typeof p.then === 'function') {
p.then(value => {
finishProgram(index, { status: 'fulfilled', value })
}).catch(reason => {
finishProgram(index, { status: 'rejected', reason })
})
} else {
finishProgram(index, { status: 'fulfilled', value: p })
}
}
})
}

Promise.any()

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static any(promises) {
let rejectCountPromise = 0
const result = []
return new Promise((resolve, reject) => {
for (let index = 0; index < promises.length; index++) {
const p = promises[index]
if (p && typeof p.then === 'function') {
p.then(value => {
resolve(value)
}).catch(err => {
result.push[err]
if (++rejectCountPromise === promises.length) {
reject(new AggregateError(result, 'All promises were rejected'))
}
})
} else {
resolve(p)
}
}
})
}

结尾

Promise 的其他静态方法及原型方法补充完毕后,下载 Promise/A+ 规范的测试用例进行校验即可。

1
pnpm add promises-aplus-tests -D