如何实现一个Promise

引言

Promise出现解决了 js 中的回调地狱的问题,使代码更简洁,是 ES6 中的规范和重要特性。它的使用很简单,但你知道它是怎么样实现的吗~~
现在我们就来看一下 Promise 究竟是怎么样实现的 😄

  • promise 规范
    Promise 规范是 Promise 函数中需要遵循的规则,
    ES6 中使用的 Promise,它就是遵循Promise/A+规范的。
    既然是有规则可循的,那我们根据规则来一步步实现Promise

1.创建 Promise 类

看一下Promise是如何使用的

1
2
3
4
5
6
7
8
9
10
11
const promise = new Promise((resolve, reject) => {
try {
resolve("123");
} catch (err) {
reject("error");
}
});

promise.then((msg) => {
console.log(msg);
});
  • 首先它是一个构造方法,并且接收一个function作为参数,
    而这个function中有resolvereject两个方法。resolve代表成功后返回的值,reject代表拒绝返回的原因

根据Promise的使用来创建一个叫MyPromise的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @params {function} callback 需要执行的业务方法
*/
class MyPromise {
// 构造方法接收一个function
constructor(callback) {
callback(this.resolve, this.reject); // 调用此function
}
resolve = (value) => {}; // callback中执行的resolve方法
reject = (reason) => {}; // callback中执行的reject方法
}

// 测试
var test = new MyPromise((resolve, reject) => {
console.log("my promise is running!");
}); // 打印出 my promise is running!

2.三种状态

现在我们创建的类已经可以执行传入的方法了,但是它传入的resolvereject方法是有什么用的呢?
我们接着看Promise 规范

  • 根据规范可知Promise有三种状态 pending(等待),fulfilled(完成),rejected(拒绝)。
  • 当状态为pending时,Promise 可以变为fulfilledrejected状态
  • 当状态为fulfilled时,Promise 不能改变其状态;必须有值且不能改变
  • 当状态为rejected时,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
const stateArr = ["pending", "fulfilled", "rejected"]; // 三种状态
/**
* @params {function} callback 需要执行的业务方法
*/
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // 当前状态
this.value = null; // 完成时的返回值
this.reason = null; // 失败原因

callback(this.resolve, this.reject); // 调用此function
}

// callback中执行的resolve方法
resolve = (value) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.value = value; // 写入最终的返回值
}
};

// callback中执行的reject方法
reject = (reason) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[2]; // 更新状态为 rejected
this.reason = reason; // 写入拒绝的原因
}
};
}

测试一下:

可以看到,调用resolve后,状态变为fulfilled,再调用reject时,状态和值都不会改变,这样符合 Promise 规范~~

3.then 方法

我们的MyPromise写到这里,他已经可以实现更新状态和传值了,但是它的值怎么样输出给我们的业务呢?
Promise的使用可以看到,它是通过then方法来输出值的。then是是一个必要的方法,看一下then的规范:

  • promise 必须提供一个then方法去访问他的当前或最终的值或原因
  • promise 中then方法接收两个参数 onFulilledonRejected

下面是关于onFulilledonRejected的规范(部分)

  • onFulilledonRejected两者都是一个可选的参数:
    • 如果onFulilled不是一个函数,它必须被忽视
    • 如果onRejected不是一个函数,它必须被忽视
  • 如果onFulilled是一个函数:
    • 它必须在 fulfilled 时被调用,promise 方法中的value作为第一个参数
    • 它必须在 fulfilled 之前被调用
    • 它不能被多次调用
  • 如果onRejected是一个函数:
    • 它必须在 rejected 时被调用,promise 方法中的reason作为第一个参数
    • 它必须在 rejected 之前被调用
    • 它不能被多次调用
  • 在执行上下文堆栈仅包含平台代码之前,不能调用onFulfilledonRejected
  • onFulfilledonRejected必须是一个函数
  • then可以在同一个 promise 中多次被调用
  • then 必须返回一个promise

根据 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
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
const stateArr = ["pending", "fulfilled", "rejected"]; // 三种状态
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // 当前状态
this.value = null; // 完成时的返回值
this.reason = null; // 失败原因

callback(this.resolve, this.reject); // 调用此function
}

// callback中执行的resolve方法
resolve = (value) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.value = value; // 写入最终的返回值
}
};

// callback中执行的reject方法
reject = (reason) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[2]; // 更新状态为 rejected
this.reason = reason; // 写入拒绝的原因
}
};

// then方法
then = (onFulilled, onRejected) => {
// 判断onFulilled 和 onRejected是否是一个函数,如果不是函数则忽略它
onFulilled =
typeof onFulilled === "function" ? onFulilled : (value) => value;
onRejected =
typeof onRejected === "function" ? onRejected : (reason) => reason;

// 如果状态是fulfilled
if (this.state === stateArr[1]) {
// then返回的必须是一个promise
return new MyPromise((resolve, reject) => {
try {
const result = onFulilled(this.value); // 执行传入的onFulilled方法

// 如果onFulilled返回的是一个Promise,则调用then方法
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});
}

// 如果状态是rejected
if (this.state === stateArr[2]) {
// then返回的必须是一个promise
return new MyPromise((resolve, reject) => {
try {
const result = onRejected(this.reason); // 执行传入的onRejected方法

// 如果onRejected返回的是一个Promise,则调用then方法
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});
}
};
}

测试一下:

成功返回:

失败返回:

4.继续完善

至此,我们的MyPromise的已经基本可以运行了,但是现在有一个很严重的缺陷,如果遇到异步的请求时候,resolve不能按上下文执行,这会导致 then 方法执行失败例如

1
2
3
4
5
6
7
8
var test = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 2000);
}).then((msg) => {
console.log(msg);
return 456;
});

因为在调用then方法的时候,promise的状态还没有改变,而我们的then方法还没有处理 pending 状态的逻辑。 这导致执行异步方法的时候,then 方法不会返回任何东西
比如,在上面的例子中,javscript 已经把then方法执行了,但setTimeout中的方法还在eventloop中等待执行。
这样需要做的是:

  • then中的方法保存起来,等待resolvereject执行后再调用刚刚保存的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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 在constructor中新增两个数组分别用于装then中的resolve和reject方法
constructor(callback) {
this.resolveArr = [];
this.rejectArr = [];
}

// 修改resolve方法
resolve = (value) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.value = value; // 写入最终的返回值

this.resolveArr.forEach(fun => fun(value)) // 循环执行then已插入的resolve方法
}
}

// 修改reject方法
reject = (reason) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.reason = reason; // 写入最终的返回值

this.rejectArr.forEach(fun => fun(reason)) // 循环执行then已插入的reject方法
}
}

// then方法中需要添加捕捉pending状态的逻辑
then = (onFulilled, onRejected) => {
// 如果状态为pending
if (this.state === stateArr[0]) {
return new Promise((resolve, reject) => {
// 插入成功时调用的函数
this.resolveArr.push((value) => {
try {
const result = onFulilled(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch(err) {
reject(err);
}
})

// 插入失败时调用的函数
this.rejectArr.push((value) => {
try {
const result = onRejected(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch(err) {
reject(err)
}
})
})
}
}

写好了,测试一下~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 2000);
})
.then((msg) => {
console.log(msg);
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(456);
}, 2000);
});
})
.then((msg) => {
console.log(msg);
});

5.Promise 的其他方法

根据 Promise 规范实现的Promise大致已经完成啦,最后我们把 Promise 中实现的方法也补一下

  • catch 方法
1
2
3
4
5
6
7
// 在MyPromise原型中实现
class MyPromise {
// 调用then中的reject
catch = (reject) => {
this.then(null, reject);
};
}
  • resolve
1
2
3
4
5
MyPromise.resolve = (value) => {
return new MyPromise((resolve, reject) => {
resolve(value);
});
};
  • reject
1
2
3
4
5
MyPromise.resolve = (reason) => {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
  • 还有allracefinally(原型方法),其实都是根据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
const stateArr = ["pending", "fulfilled", "rejected"]; // 三种状态
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // 当前状态
this.value = null; // 完成时的返回值
this.reason = null; // 失败原因
this.resolveArr = [];
this.rejectArr = [];

callback(this.resolve, this.reject); // 调用此function
}

// callback中执行的resolve方法
resolve = (value) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.value = value; // 写入最终的返回值

this.resolveArr.forEach((fun) => fun(value)); // 循环执行then已插入的resolve方法
}
};

// callback中执行的reject方法
reject = (reason) => {
// 判断状态是否需要是pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; // 更新状态为 fulfilled
this.reason = reason; // 写入最终的返回值

this.rejectArr.forEach((fun) => fun(reason)); // 循环执行then已插入的reject方法
}
};

// then方法
then = (onFulilled, onRejected) => {
// 判断onFulilled 和 onRejected是否是一个函数,如果不是函数则忽略它
onFulilled =
typeof onFulilled === "function" ? onFulilled : (value) => value;
onRejected =
typeof onRejected === "function" ? onRejected : (reason) => reason;

// 如果状态为pending
if (this.state === stateArr[0]) {
return new MyPromise((resolve, reject) => {
// 插入成功时调用的函数
this.resolveArr.push((value) => {
try {
const result = onFulilled(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});

// 插入失败时调用的函数
this.rejectArr.push((value) => {
try {
const result = onRejected(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});
});
}

// 如果状态是fulfilled
if (this.state === stateArr[1]) {
// then返回的必须是一个promise
return new MyPromise((resolve, reject) => {
try {
const result = onFulilled(this.value); // 执行传入的onFulilled方法

// 如果onFulilled返回的是一个Promise,则调用then方法
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});
}

// 如果状态是rejected
if (this.state === stateArr[2]) {
// then返回的必须是一个promise
return new MyPromise((resolve, reject) => {
try {
const result = onRejected(this.reason); // 执行传入的onRejected方法

// 如果onRejected返回的是一个Promise,则调用then方法
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
});
}
};

// 调用then中的reject
catch = (reject) => {
this.then(null, reject);
};
}

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

MyPromise.resolve = (reason) => {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};

小结

这次我们了解了 promise 是如何实现的:

  • 必须是构造函数
  • 三种状态(pending,resolve,reject)
  • then 方法(promise 中必须要有的方法)

从构造函数开始,到三种状态的实现,最后实现 then 方法一步步根据Promise规则来实现 Promise。了解完以后就可以在面试官面前手写一个 Promise 啦!😄