JavaScript 中 Promise 的实现

JavaScript 中 Promise 的实现

之前重新梳理了一下 JavaScript 中的 Promise 的相关内容,也算是又温习了一遍 Promise 相关知识点,本章当中我们就来看看关于 Promise 的最后一部分内容,也就是 Promise 的内部实现原理

参考了网上各路大神的实现方式,发现虽然实现方式各有不同,但是原理都是十分类似的,所以下面就让我们站在巨人的肩膀上来实现一个我们自己版本的 Promise,尽量做到浅显易懂

基本结构

我们先来看一个官方版本的 Promise 的简单使用方式,如下

1
2
3
4
5
6
7
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result')
}, 1000)
})

p1.then(res => console.log(res), err => console.log(err))

观察上面这个示例我们可以发现,构造函数 Promise 必须接受一个函数作为参数,我们称该函数为 executorexecutor 又包含 resolvereject 两个参数,它们是两个函数,而内部的异步任务(这里是 setTimeout())则会被放入对应任务队列,等待我们去调用 then() 来进行执行

所以我们可以明显的意识到这其实就是一个观察者(收集依赖 => 触发通知 => 取出依赖执行)模式,所以我们可以依据这个流程得出我们的 Promise 大致模样

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
class MyPromise {

constructor(executor) {
if (!isFunction(executor)) {
throw new Error(`MyPromise must accept a function as a parameter`)
}
this._fulfilledQueues = [] // 成功回调函数队列
this._rejectedQueues = [] // 失败回调函数队列
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}

_resolve(val) {
// 依次执行成功队列中的函数,并清空队列
while (this._fulfilledQueues.length) {
const callback = this._fulfilledQueues.shift()
callback(val)
}
}

_reject(val) {
// 依次执行成功队列中的函数,并清空队列
while (this._rejectedQueues.length) {
const callback = this._rejectedQueues.shift()
callback(val)
}
}
}

这里的 isFunction 就是一个简单的判断参数是否为函数的方法,如下

1
const isFunction = fn => typeof fn === 'function'

我们建立了两个回调队列,将每次 then 方法注册时的回调函数添加到数组中等待执行,之所以使用一个队列来储存回调,是因为我们知道 then 方法可以被同一个 Promise 调用多次,所以如果使用一个变量而非队列来储存回调,那么即使多次 p1.then() 也只会执行一次回调,其次当 resolvereject 方法执行时,我们可以依次提取成功或失败任务队列当中的函数来进行执行,并清空队列,从而实现 then 方法的多次调用

但是我们在平常在使用 Promise 的时候,都知道它内部是含有多个状态的,所以下面我们再来看看如何给我们的 Promise 添加状态

Promises/A+ 规范

我们都知道,Promise 对象存在以下三种状态

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

之所以有这几种状态,是因为 ES6 中的 Promise 的实现需要遵循 Promises/A+ 规范,是规范对 Promise 的状态控制做了要求,规范当中涉及到的内容比较多,在这里我们只总结两条核心规则,如下

  1. Promise 本质是一个状态机,且状态只能为以下三种,即 Pending(等待态)、Fulfilled(执行态)和 Rejected(拒绝态),状态的变更是单向的,只能从 Pending => Fulfilled 或者 Pending => Rejected,并且状态的变更不可逆
  2. then 方法接收两个可选参数,分别对应状态改变时触发的回调,then 方法返回一个新的 Promise,并且可以被同一个 Promise 调用多次

如下图所示

所以根据规范,我们再来补充一下我们的 Promise 代码,添加状态和值,并添加状态改变的执行逻辑

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
50
const isFunction = fn => typeof fn === 'function'
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {

constructor(executor) {
if (!isFunction(executor)) {
throw new Error(`MyPromise must accept a function as a parameter`)
}
this._status = PENDING // 当前状态
this._value = undefined // 回调返回的值
this._fulfilledQueues = [] // 成功回调函数队列
this._rejectedQueues = [] // 失败回调函数队列
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}

_resolve(val) {
// 我们将上面的 _resolve 执行回调的操作封装成一个函数,放进 setTimeout 里,以兼容 executor 是同步代码的情况
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
this._value = val
while (this._fulfilledQueues.length) {
const callback = this._fulfilledQueues.shift()
callback(val)
}
}
setTimeout(run)
}

_reject(err) {
// 同理
const run = () => {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
while (this._rejectedQueues.length) {
const callback = this._rejectedQueues.shift()
callback(val)
}
}
setTimeout(run)
}
}

为了支持同步的 Promise,我们将之前同步的回调放到了一个 run 函数当中,并且采用了异步调用的方式,这样一来我们就实现了 Promise 状态和值的改变,下面我们再来看看 Promise 的核心方法 then 的实现

then 方法

Promise 对象的 then 方法接受两个参数,一个成功的回调和一个失败的回调

1
promise.then(onFulfilled, onRejected)

我们还是和上面一样,根据 Promises/A+ 规范来梳理一下 then 方法实现当中需要注意的地方,首先 onFulfilledonRejected 都是可选参数,如果 onFulfilledonRejected 不是函数,需要被忽略

  • onFulfilled 的特性
    • 如果 onFulfilled 不是函数,忽略
    • Promise 状态变为成功时必须被调用,其第一个参数为 Promise 成功状态传入的值(resolve 执行时传入的值)
    • Promise 状态改变前其不可被调用
    • 其调用次数不可超过一次
  • onRejected 的特性
    • 如果 onRejected 不是函数,忽略
    • Promise 状态变为失败时必须被调用,其第一个参数为 Promise 失败状态传入的值(reject 执行时传入的值)
    • Promise 状态改变前其不可被调用
    • 其调用次数不可超过一次

其次我们还需要注意多次调用的情况,因为 then 方法可以被同一个 Promise 对象调用多次,所以在这里需要注意

  • Promise 成功状态时,所有 onFulfilled 需按照其注册顺序依次回调
  • Promise 失败状态时,所有 onRejected 需按照其注册顺序依次回调

最后需要注意的就是 then 方法调用以后的返回值

1
promise2 = promise1.then(onFulfilled, onRejected)

因为 then 方法必须返回一个新的 Promise 对象,所以 Promise 才可以支持链式调用

1
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2)

关于多次调用和返回值,因为这里涉及到了 Promise 的执行规则,也就是值的传递和错误捕获机制,所以这里我们借住规范来详细梳理一下,主要有以下几点

  1. 如果 onFulfilled 或者 onRejected 返回一个值 x,则运行下面的 Promise 解决过程([[Resolve]](promise2, x)

    • x 不为 Promise,则使 x 直接作为新返回的 Promise 对象的值,即新的 onFulfilled 或者 onRejected 函数的参数
    • xPromise,这个时候后一个回调函数就会等待该 Promise 对象(即 x)的状态发生变化才会被调用,并且新的 Promise 状态和 x 的状态相同

其实简单来说就是 then 方法的返回值分为普通值和 Promise 对象两种情况,所以需要进行不同的处理,对比以下两个示例,我们就能很明显的发现其中的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})

promise2 = promise1.then(res => {
return '这里返回一个普通值'
})

promise2.then(res => {
// `这里返回一个普通值`
console.log(res)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})

promise2 = promise1.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('这里返回一个 Promise')
}, 2000)
})
})

promise2.then(res => {
// `这里返回一个 Promise`
console.log(res)
})
  1. 如果 onFulfilled 或者 onRejected 抛出一个异常 e,则 promise2 必须变为失败(Rejected),并返回失败的值 e,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})

promise2 = promise1.then(res => {
throw new Error('这里抛出一个异常 e')
})

promise2.then(res => {
console.log(res)
}, err => {
// `这里抛出一个异常 e`
console.log(err)
})
  1. 如果 onFulfilled 不是函数且 promise1 状态为成功(Fulfilled),promise2 必须变为成功(Fulfilled)并返回 promise1 成功的值,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})

promise2 = promise1.then('这里的 onFulfilled 本来是一个函数,但现在不是')

promise2.then(res => {
// success
console.log(res)
}, err => {
console.log(err)
})

针对于这种情况,简单来说就是 then 方法接收的参数如果不是函数,那么我们应该忽略它,如果没有忽略的话,当 then 方法回调不为函数的时候将会抛出异常,导致链式调用中断

  1. 如果 onRejected 不是函数且 promise1 状态为失败(Rejected),promise2 必须变为失败(Rejected)并返回 promise1 失败的值,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})

promise2 = promise1.then(res => res, '这里的 onRejected 本来是一个函数,但现在不是')

promise2.then(res => {
console.log(res)
}, err => {
// fail
console.log(err)
})

其实梳理下来我们可以发现,上面的一些处理方式,其实都是为了解决状态分别为 resolve/reject 下的不同情况,因为在有些时候 resolve/reject 可能在 then() 之前就被执行(比如 Promise.resolve().then()),如果这个时候我们还把 then() 回调 pushresolve/reject 的执行队列里,那么回调将不会被执行,因此对于状态已经变为 fulfilledrejected 的情况,我们需要单独来进行处理,所以下面我们就来看看到底该如何实现

在了解了以上总结的一些规则以后,我们就可以得出 then 方法的大概雏形,首先 then 方法接受两个函数作为参数,然后返回一个新的 Promise 对象,并且需要将回调函数加入到执行队列中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
then(onFulfilled, onRejected) {
const { _value, _status } = this
switch (_status) {
// 当状态为 PENDING 时,将 then 方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
onFulfilled(_value)
break
case REJECTED:
onRejected(_value)
break
}

return new MyPromise((onFulfilledNext, onRejectedNext) => {
// ...
})
}

这个时候我们就需要考虑一下这个返回的对象了,首先返回的新的 Promise 对象肯定是包含两个函数的,也就是成功时执行的函数和失败时执行的函数,但是我们什么时候改变状态呢?改变为哪种状态呢?

根据上面 then 方法当中的规则,我们知道返回的新的 Promise 对象的状态依赖于当前 then 方法回调函数执行的情况以及返回值,例如 then 方法的参数是否为一个函数、回调函数执行是否出错、返回值是否为 Promise 对象等等,也就是说,在这里我们就不能简单的针对于成功或者失败的状态而去直接调用传递进来的 onFulfilled() 或者 onRejected() 方法了,而是需要根据当前状态的不同,进行不同的处理

如下,我们将对应的 onFulfilled()onRejected() 包裹一层,也就是下面的 fulfilledrejected 方法,在其中我们针对当前状态的不同来进行不同的处理

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) {
const { _value, _status } = this
return new MyPromise((onFulfilledNext, onRejectedNext) => {
let fulfilled = value => { // 成功时执行的函数
try {
if (!isFunction(onFulfilled)) { // 如果不是函数,直接在成功回调当中返回当前值
onFulfilledNext(value)
} else {
let res = onFulfilled(value) // 成功回调返回的值
if (res instanceof MyPromise) { // 如果返回的是 MyPromise 对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res) // 否则会将返回结果直接作为参数,传入下一个 then 的回调函数,并立即执行下一个 then 的回调函数
}
}
} catch (err) { // 如果函数执行出错,新的 Promise 对象的状态为失败
onRejectedNext(err)
}
}
let rejected = error => { // 失败时执行的函数,逻辑和上面是一样的
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error)
if (res instanceof MyPromise) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_status) {
case PENDING: // 当状态为 PENDING 时,将 then 方法回调函数加入执行队列等待执行
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
case FULFILLED: // 当状态已经改变时,立即执行对应的回调函数
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}

最后我们再来考虑处理一种特殊情况,那就是如果 _resolve 方法传入的参数为一个 Promise 对象,并且此时该 Promise 对象状态决定当前 Promise 对象的状态,也就是下面这个示例当中所示

1
2
3
4
5
6
7
8
const p1 = new Promise(function (resolve, reject) {
// ...
})

const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1)
})

上面代码中,p1p2 都是 Promise 的实例,但是 p2resolve 方法将 p1 作为参数,即一个异步操作的结果是返回另一个异步操作,这时 p1 的状态就会传递给 p2,也就是说 p1 的状态决定了 p2 的状态,也就是

  • 如果 p1 的状态是 Pending,那么 p2 的回调函数就会等待 p1 的状态改变
  • 如果 p1 的状态已经是 Fulfilled 或者 Rejected,那么 p2 的回调函数将会立刻执行

所以下面我们就来修改我们开头部分的 _resolve 方法以便支持这样的特性,我们将 _resolve 当中单纯的执行成功队列中的函数的方式拆分了一下,分为了 runFulfilledrunRejected 两种情况,然后根据传递进 _resolve 方法的参数不同来分别赋予不同的状态和执行逻辑

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
_resolve(val) {
const run = () => {
if (this._status !== PENDING) return
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
while (this._fulfilledQueues.length) {
const callback = this._fulfilledQueues.shift()
callback(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
while (this._rejectedQueues.length) {
const callback = this._rejectedQueues.shift()
callback(error)
}
}
// 如果 resolve 的参数为 Promise 对象,则必须等待该 Promise 对象状态改变后,当前 Promsie 的状态才会改变
// 且状态取决于参数 Promsie 对象的状态
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
this._status = FULFILLED
runFulfilled(value)
}, err => {
this._value = err
this._status = REJECTED
runRejected(err)
})
} else {
this._value = val
this._status = FULFILLED
runFulfilled(val)
}
}
setTimeout(run, 0)
}

至此,我们已经实现了 Promise 的主要功能,但是原生的 Promise 还提供了一些额外方法,下面我们来看看这些附加方法如何实现

catch()

我们在之前的 JavaScript 中的 Promise 章节当中的曾经提到过,实际上 catch() 只是 promise.then(undefined, onRejected) 方法的一个别名而已,但是需要注意的是 catch() 方法也返回一个 Promise,也就是说 catch 之后,我们还是可以继续是使用 then 方法

1
2
3
4
// 其实就是执行 then 的第二个回调
catch (onRejected) {
return this.then(undefined, onRejected)
}

resolve()

Promise.resolve(value) 方法返回一个以给定值解析后的 Promise 对象

  • 如果 value 是个 Thenable 对象,返回的 Promise 会跟随这个 Thenable 的对象,采用它的最终状态(关于 Thenable 对象可以参考 JavaScript 中的 Promise
  • 如果传入的 value 本身就是 Promise 对象,那么 Promise.resolve 将不做任何修改、原封不动地返回这个 Promise 对象
  • 其他情况则直接返回以该值为成功状态的 Promise 对象
1
2
3
4
5
static resolve(value) {
// 如果参数是 MyPromise 实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}

reject()

Promise.resolve() 不同的是,Promise.reject() 方法的参数会原封不动地作为 reject 的理由,变成后续方法的参数

1
2
3
static reject(value) {
return new MyPromise((resolve, reject) => reject(value))
}

all()

Promise.all(promises) 返回一个 Promise 对象

  • 如果传入的参数是一个空的可迭代对象,那么此 Promise 对象回调完成(resolve),只有此情况是同步执行的,其它都是异步返回的
  • 如果传入的参数不包含任何 Promise,则返回一个异步完成
  • 所有的 Promise 都完成时或参数中不包含 Promise 时回调完成
  • 如果参数中有一个 Promise 失败,那么 Promise.all 返回的 Promise 对象失败,失败原因是第一个失败 Promise 的结果
  • 在任何情况下,Promise.all 返回的 Promise 的完成状态的结果都是一个数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static all(list) {
return new MyPromise((resolve, reject) => {
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是 MyPromise 实例,先调用 MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成 fulfilled 时返回的 MyPromise 状态就变成 fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被 rejected 时返回的 MyPromise 状态就变成 rejected
reject(err)
})
}
})
}

race()

Promise.race() 函数返回一个 Promise,它将与第一个传递的 Promise 相同的完成方式被完成,它可以是完成(resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个,如果传的参数数组是空,则返回的 Promise 将永远等待

1
2
3
4
5
6
7
8
9
10
11
12
static race(list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的 MyPromise 的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}

finally()

finally() 方法返回一个 Promise,在 Promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数,在 finally 之后,我们还可以继续调用 then 方法,并且会将值原封不动的传递给后面的 then 方法

1
2
3
4
5
6
finally (callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
)
}

这里针对 MyPromise.resolve(callback()) 我们详细说明一下,这个写法其实涉及到一个 finally() 的使用细节,即 finally() 如果 return 了一个 reject 状态的 Promise,将会改变当前 Promise 的状态,这个 MyPromise.resolve() 就用于改变 Promise 状态,在 finally() 没有返回 reject 状态的 Promisethrow 错误的情况下,去掉 MyPromise.resolve 也是一样的

比如下面这个例子

1
2
3
4
5
6
7
8
9
var p = Promise.resolve('ok')
.finally(() => {
return Promise.reject('这里只有返回被拒绝的 promise 或者 throw 一个错误,才会影响当前 finally 返回的新 promise 的决议')
})
.then(value => {
console.log('成功', value)
}, (err) => {
console.log('失败', err)
})

finally() 对自身返回的 Promise 的决议影响有限,它可以将上一个 resolve 改为 reject,也可以将上一个 reject 改为另一个 reject,但不能把上一个 reject 改为 resolve,更多使用细节可以参考 Promise.prototype.finally()

完整代码

以上,我们就实现了一个完整的 Promsie,更为完善的实现可以参考 es6-promise 这个版本,也算是 Promise 的众多实现中较为完善的一个例子,下面来看看我们汇总后的代码,如下

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
const isFunction = fn => typeof fn === 'function'
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
constructor(executor) {
if (!isFunction(executor)) {
throw new Error('MyPromise must accept a function as a parameter')
}

this._status = PENDING // 当前状态
this._value = undefined // 回调返回的值
this._fulfilledQueues = [] // 成功回调函数队列
this._rejectedQueues = [] // 失败回调函数队列
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}

_resolve(val) {
// 我们把 _resolve 执行回调的操作封装成一个函数,放进 setTimeout 里,以兼容 executor 是同步代码的情况
const run = () => {
if (this._status !== PENDING) return
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
while (this._fulfilledQueues.length) {
const callback = this._fulfilledQueues.shift()
callback(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
while (this._rejectedQueues.length) {
const callback = this._rejectedQueues.shift()
callback(error)
}
}
// 如果 resolve 的参数为 Promise 对象,则必须等待该 Promise 对象状态改变后,当前 Promsie 的状态才会改变,且状态取决于参数 Promsie 对象的状态
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
this._status = FULFILLED
runFulfilled(value)
}, err => {
this._value = err
this._status = REJECTED
runRejected(err)
})
} else {
this._value = val
this._status = FULFILLED
runFulfilled(val)
}
}
setTimeout(run, 0)
}

_reject(err) {
// 依次执行失败队列中的函数,并清空队列
const run = () => {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
while (this._rejectedQueues.length) {
const callback = this._rejectedQueues.shift()
callback(err)
}
}
setTimeout(run, 0)
}


then(onFulfilled, onRejected) {
const { _value, _status } = this
return new MyPromise((onFulfilledNext, onRejectedNext) => {
let fulfilled = value => { // 成功时执行的函数
try {
if (!isFunction(onFulfilled)) { // 如果不是函数,直接在成功回调当中返回当前值
onFulfilledNext(value)
} else {
let res = onFulfilled(value) // 成功回调返回的值
if (res instanceof MyPromise) { // 如果返回的是 MyPromise 对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res) // 否则会将返回结果直接作为参数,传入下一个 then 的回调函数,并立即执行下一个 then 的回调函数
}
}
} catch (err) { // 如果函数执行出错,新的 Promise 对象的状态为失败
onRejectedNext(err)
}
}
let rejected = error => { // 失败时执行的函数,逻辑和上面是一样的
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error)
if (res instanceof MyPromise) {
res.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(res)
}
}
} catch (err) {
onRejectedNext(err)
}
}
switch (_status) {
// 当状态为 PENDING 时,将 then 方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}

catch(onRejected) {
// 其实就是执行 then 的第二个回调
return this.then(undefined, onRejected)
}

static resolve(value) {
// 如果参数是 MyPromise 实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}

static reject(value) {
return new MyPromise((resolve, reject) => reject(value))
}

static all(list) {
return new MyPromise((resolve, reject) => {
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是 MyPromise 实例,先调用 MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成 fulfilled 时返回的 MyPromise 状态就变成 fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被 rejected 时返回的 MyPromise 状态就变成 rejected
reject(err)
})
}
})
}

static race(list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的 MyPromise 的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}

finally(callback) {
return this.then(
//执行回调,并returnvalue传递给后面的then
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
)
}
}

最后我们来简单的测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义一个 Promise
const p1 = new MyPromise((resolve, reject) => {
resolve(1)
})

// 链式调用
p1.then(res => {
console.log(res)
return 2
}).then('123').then(res => {
// `123` 会被忽略
console.log(res)
// 返回一个新的 Promise
return new MyPromise((resolve, reject) => {
resolve(3)
})
}).then(res => {
console.log(res)
// 抛出错误
throw new Error('reject测试')
}).then(() => { }, err => {
console.log(err)
})

输出结果如下

1
2
3
4
// 1 
// 2
// 3
// Error: reject 测试

参考

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×