# JS 理解 Promise
前段时间刷到了一个面试题,手写一个 Promise,发现要实现一个符合规范的 Promise 还真不简单,所以单独抽离出来总结一下。
# Promise/A+ 规范解读
要实现一个 Promise,首先应该遵守一下 Promise/A+ 规范规范,由于规范涉及面很广,这里就不详细介绍了,只介绍一下最核心的部分。
# Promise 的状态
Promise 的状态只有 3 中:等待态(pending)、完成态(fulfilled)或拒绝态(rejected)。
Promise 中的状态只能按以下方式改变:一旦状态发生改变,就定死了,再也不能改变了。
- pending -> fulfilled
- pending -> rejected
# Promise 的 then 方法
then 方法需要传递 2 个函数,分别为 Fulfilled 状态和 Rejected 状态的回调函数。
promise.then(onFulfilled, onRejected)
1
- 如果 onFulfilled 和 onRejected 不是函数,则会被忽略。
- 如果 onFulfilled 是一个函数,则当 Promise 状态转变成 Fulfilled 时,会被触发,并能获取 Resolve 中传递的值。
- 如果 onRejected 是一个函数,则当 Promise 状态转变成 Rejected 时,会被触发,并能获取 Reject 中传递的值。
- then 方法可以被多次调用,按照调用顺序执行代码。
- then 方法同样返回一个 Promise 对象。
# Promise 的解析过程
当 Promise 的 resolve 方法调用时,会传入一个值,Promise 会根据值类型的不同,进行不同的处理。
- 如果 resolve 的参数是 Promise 本身,则会报循环 Promise 错误。
- 如果 resolve 的参数是一个 新 Promise,则会等待新 Promise 的状态改变后,返回新 Promise 的值。
- 如果新 Promise 的状态不是 Fulfilled 或者 Resolve,调用新 Promise 的 then 方法,直到状态改变为止。
- 如果 resolve 的参数是一个对象或者函数,会检查是否含有 then 方法,如果有,则会被当成一个新 Promise 处理(通过上一步的方式处理)。
- 其他情况,则会直接返回 resolve 中的值。
# MyPromise 最终代码
const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';
function MyPromise(fn) {
// 初始化状态
const self = this;
self.state = PENDING;
self.value = undefined;
self.resolvedCallbacks = [];
self.rejectedCallbacks = [];
// 执行promise 函数,传入resolve
try {
fn(resolve, reject);
} catch (reason) {
reject(reason);
}
// 根据传递的resolve值,更新状态
function resolve(val) {
if (val instanceof MyPromise) {
val.then(resolve, reject);
}
setTimeout(() => {
if (self.state === PENDING) {
self.value = val;
self.state = RESOLVED;
self.resolvedCallbacks.map(fn => fn(val));
}
}, 0);
}
function reject(val) {
setTimeout(() => {
if (self.state === PENDING) {
self.value = val;
self.state = REJECTED;
self.rejectedCallbacks.map(fn => fn(val));
}
}, 0);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
// 处理then穿透
onResolved = typeof onResolved === 'function' ? onResolved : v => v;
onRejected =
typeof onRejected === 'function'
? onRejected
: v => {
throw new Error(v);
};
var self = this;
var promise2;
if (self.state === RESOLVED) {
promise2 = new MyPromise(function(resolve, reject) {
setTimeout(() => {
var x = onResolved(self.value);
resolvePromise(promise2, x, resolve, reject);
}, 0);
});
}
if (self.state === REJECTED) {
promise2 = new MyPromise(function(resolve, reject) {
setTimeout(() => {
var x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
}, 0);
});
}
if (self.state === PENDING) {
promise2 = new MyPromise(function(resolve, reject) {
self.resolvedCallbacks.push(function() {
var x = onResolved(self.value);
resolvePromise(promise2, x, resolve, reject);
});
self.rejectedCallbacks.push(function() {
var x = onRejected(self.value);
resolvePromise(promise2, x, resolve, reject);
});
});
}
return promise2;
};
// 这个函数主要是用于判断Promise的状态,根据resolve 传递的值来做不同判断。
function resolvePromise(promise2, x, resolve, reject) {
var isCalled = false;
if (promise2 === x) {
throw new TypeError('循环引用');
}
// 如果resolve 返回的任然是一个新Promise,就会调用then,返回新promise的状态
if (x instanceof MyPromise) {
if (x.state === PENDING) {
x.then(function(y) {
resolvePromise(promise2, y, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 如果resolve的是一个函数或者对象,会检查是否有then属性,如果有,则会被当成一个类promise对象进行操作
// 具体操作,调用then方法,并递归调用resolvePromise函数检查状态。
var then = x.then;
if (then) {
try {
then.call(
x,
function(y) {
resolvePromise(promise2, y, resolve, reject);
},
function(r) {
return reject(r);
}
);
} catch (reason) {
reject(reason);
}
} else {
resolve(x);
}
} else {
resolve(x);
}
}
MyPromise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
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
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
# 相关链接
← 流量劫持 JS 理解 This →