Promise原理手写实现

已实现方法

  • p.then()
  • p.catch()
  • Promise.all()
  • Promise.race()

代码演示

Talk is cheap, show me the code!

new 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
function MyPromise(executor) {
this.status = 'pending'
this.resolvedValue = null
this.rejectedValue = null
this.onFullFilledList = []
this.onRejectedList = []

const _this = this

function resolve(value) {
if (_this.status === 'pending') {
_this.status = 'fulfilled'
_this.resolvedValue = value

_this.onFullFilledList.forEach((fn) => fn(value))
}
}

function reject(value) {
if (_this.status === 'pending') {
_this.status = 'reject'
_this.rejectedValue = value

_this.onRejectedList.forEach((fn) => fn(value))
}
}

try {
executor(resolve, reject)
} catch (err) {
console.log(err)
}
}

.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
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
// * Then方法实现
MyPromise.prototype.then = function (onFulfilled, onRejected) {
// * 入参处理
if (!isFunction(onFulfilled)) onFulfilled = (value) => value
if (!isFunction(onRejected)) onRejected = (reason) => throw reason

// * Note: Then method simple implementation
// if (this.status === 'fulfilled') {
// onFulfilled(this.resolvedValue)
// }

// if (this.status === 'reject') {
// onRejected(this.rejectedValue)
// }

// // 这里处理当resolve或reject为异步调用的情况
// // 所以我们此时应该把当前传递的函数保存到一个数组 在真正resolve或reject的时候调用这些函数
// if (this.status === 'pending') {
// this.onFullFilledList.push(onFulfilled)
// this.onRejectedList.push(onRejected)
// }
// * Note: Simple implementation done.

// * Note: Then method standard implementation
// then方法要支持链式调用 需返回一个promise实例
// 分析:
// 1. 要支持then的链式调用 考虑前一个then的中的处理结果
// 1.1 .then((data) => console.log(data)) 返回结果undefined
// 1.2 返回结果可能是任意值 分情况判断

const taskOfFulfilled = () => {
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const prevResult = onFulfilled(this.resolvedValue)
handlePromise(promise2, prevResult, resolve, reject)
} catch (err) {
reject(err)
}
})
}

const taskOfRejected = () => {
queueMicrotask(() => {
try {
// 获取失败回调函数的执行结果
const prevResult = onRejected(this.rejectedValue)
handlePromise(promise2, prevResult, resolve, reject)
} catch (err) {
reject(err)
}
})
}

const promise2 = new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
// promise2未初始化就使用 报错:Cannot access 'promise2' before initialization
// prevResult存在几种情况
// const prevResult = onFulfilled(this.resolvedValue)
// handlePromise(promise2, prevResult, resolve, reject)

// * 针对上述报错修改
// 创建一个微任务等待promise2完成初始化
// @see:https://developer.mozilla.org/zh-CN/docs/Web/API/queueMicrotask
taskOfFulfilled()
}

if (this.status === 'reject') taskOfRejected()

if (this.status === 'pending') {
this.onFullFilledList.push(taskOfFulfilled)
this.onRejectedList.push(taskOfRejected)
}
})

return promise2
// * Note: Then method normal implementation done
}

handlePromise()

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
function handlePromise(promise2, prevResult, resolve, reject) {
// then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错
if (promise2 === prevResult) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}

if (typeof prevResult === 'object' && prevResult !== null) {
let then = prevResult?.then
try {
if (typeof then === 'function') {
// called 变量主要是用来判断如果 handlePromise 函数已经 resolve 或者 reject了,那就不需要在执行下面的 resolve 或者 reject
let called = false
// call方法 foo.call(object,arg1, arg2, ...) 成功或失败的回调函数作为参数传入
then.call(
prevResult, // this指向问题
(res) => {
if (called) return
called = true
// 递归 直到值不再是promise
handlePromise(promise2, res, resolve, reject)
},
(err) => {
if (called) return
called = true
reject(err)
}
)
} else {
resolve(prevResult)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
// 普通值处理
resolve(prevResult)
}
}

.catch()

1
2
3
MyPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}

Promise.all()

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
// * Promise.all方法实现
// 传入一个数组 且都是promise实例
// 返回一个promise then中的内容分别是数组promises中promise之后的结果
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
if (!promises || !Array.isArray(promises)) return reject()
const len = promises.length
const result = []

const process = (data) => {
result.push(data)
if (len === result.length) resolve(result)
}

for (const promise of promises) {
if (isPromise(promise)) {
promise.then(
(data) => {
process(data)
},
(err) => {
return reject(err)
}
)
} else {
// 非promise的情况 原样返回
process(promise)
}
}
})
}

Promise.race()

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
// * Promise.race方法实现 竞速
// 传入一个数组 且都是promise实例
// 返回一个promise 哪个实例先resolve就返回
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
if (!promises || !Array.isArray(promises)) return reject()

for (const promise of promises) {
if (isPromise(promise)) {
promise.then(
(data) => {
resolve(data)
return
},
(err) => {
reject(err)
return
}
)
} else {
resolve(promise)
}
}
})
}

Utils-工具函数

isFunction()

1
2
3
function isFunction(o)  {
return typeof o === 'function'
}

isPromise()

1
2
3
4
5
6
7
function isPromise(promise) {
return (
typeof promise === 'object' &&
promise !== null &&
typeof promise.then === 'function'
)
}

Test Cases

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
// * 说明:下面示例主要考虑resolve的情况 reject同理
const p = new MyPromise((resolve, reject) => {
// Test2: resolve异步调用的情况
setTimeout(() => {
resolve(100)
// reject('This a test of reject')
}, 2000)

// Test1: resolve直接同步调用
// resolve(100)
})

// then方法返回自己的promise时报错 Chaining cycle detected for promise #<Promise>
// const p1 = p.then(resolve => p1)


// * Test1
// Done. 2s之后输出 data: 100
// p.then(
// (data) => {
// console.log('data:', data)
// },
// (err) => {
// console.log('err:', err)
// }
// )

// * Test2 多次调用then方法
// Done. 2s之后输出2次 data: 100
// p.then((data) => {
// console.log('data:', data)
// })

// p.then((data) => {
// console.log('data:', data)
// })

// * Test3 then的链式调用
// Done. 2s之后输出2次 data: 100 data: undefined
// p.then((data) => {
// console.log('data:', data)
// }).then((data) => {
// console.log('data:', data)
// })

总结

1
2
3
// ? 问题
// 1. 当new Promise中直接同步调用resolve时 可以拿到值,但如果存在异步,此时调用then方法中 status处于pending状态
// 2. then链式调用