0%

手写一个Promise

Promise/A+

下面是一个简单的Promise的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const promise1 = new Promise((resolve, reject) => {
console.log(1);
resolve(2);
console.log(3);
reject(4);
});

const promise2 = new Promise((resolve, reject) => {
reject(5);
});

console.log(6);

promise1.then(console.log, console.log);
promise2.then(console.log, console.log);

它对应的输出结果为

1
2
3
4
5
1
3
6
2
5

结合Promise/A+规范,可以总结出Promise基本特征如下所示(promise表示Promise的实例)

  1. new Promise时,需要传入一个立即执行函数executor
  2. executor接受两个异步执行的回调函数,分别是成功后的回调resolve和失败后的回调reject
  3. promise只有三个状态,pendingfulfilledrejected
  4. promise初始状态为pending
  5. promise只能从pending修改为fulfilled或者rejected,修改以后不可逆,无法再次修改
  6. promise有一个value属性,用来保存成功后返回的值
  7. promise有一个reason属性,用来保存失败后返回的值
  8. promise有一个then方法,接收 Promise成功的回调onFulfilledPromise失败的回调onRejected

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
const PromiseStatusEnum = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
};

class MyPromise {
constructor(executor) {
this.status = PromiseStatusEnum.PENDING;
this.value = null;
this.reason = null;
this.resolveQueue = [];
this.rejectQueue = [];
}

then = (onFulfilled, onRejected) => {};

catch = (onRejected) => {};

finally = (callback) => {};

static resolve = (value) => {};

static reject = (error) => {};

static all = (promiseArr) => {};

static race = (promiseArr) => {};
}

executor

executor接受两个回调函数resolvereject,通过箭头函数绑定当前实例的this,通过setTimeout异步执行

1
2
3
4
5
6
7
8
9
const resolve = (value) => {
const run = () => {};
setTimeout(run);
};

const reject = (error) => {
const run = () => {};
setTimeout(run);
};

run主要负责

  1. 判断当前的状态是否为pending,并对其修改
  2. 记录成功或者失败的返回值

基本的executor如下

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
const resolve = (value) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.FULFILLED;
this.value = value;
}
};
setTimeout(run);
};

const reject = (error) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.REJECTED;
this.reason = error;
}
};
setTimeout(run);
};

//兼容executor的异常情况
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}

then

then接受两个回调函数onFulfilledonRejected,并返回一个Promise对象

1
2
3
4
5
6
7
then = (onFulfilled, onRejected) => {
// onFulfilled或者onRejected不是函数时,返回当前的值
typeof onFulfilled !== 'function' ? (onFulfilled = (value) => value) : null;
typeof onRejected !== 'function' ? (onRejected = (error) => error) : null;

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

再根据promise的状态进行不同的操作

  1. state === 'fulfilled'时,执行 onFulfilled
  2. state === 'rejected'时,执行 onRejected
  3. 如果在执行回调函数时抛出了异常,那么就会把这个异常作为参数,直接reject
  4. 回调函数返回的值不是Promise,直接resolve
  5. 回调函数返回的值是Promise,调用then方法,保证Promise会被全部执行
  6. state === 'pending'时,需要将回调函数放入队列中,等待执行
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
then = (onFulfilled, onRejected) => {
return new MyPromise((resolve, reject) => {
const resolveFn = (value) => {
try {
const x = onFulfilled(value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};

const rejectFn = (error) => {
try {
const x = onRejected(error);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};

switch (this.status) {
case PromiseStatusEnum.PENDING:
this.resolveQueue.push(resolveFn);
this.rejectQueue.push(rejectFn);
break;
case PromiseStatusEnum.FULFILLED:
resolveFn(this.value);
break;
case PromiseStatusEnum.REJECTED:
rejectFn(this.reason);
break;
}
});
};

resolveQueuerejectQueue收集的回调,需要在executor中的resolvereject执行

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
const resolve = (value) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.FULFILLED;
this.value = value;

while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(value);
}
}
};

setTimeout(run);
};
const reject = (error) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.REJECTED;
this.reason = error;

while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(error);
}
}
};

setTimeout(run);
};

catch

实现了then以后,catch就比较简单了,直接调用thenonFulfilled传空就行了

1
catch = (onRejected) => this.then(undefined, onRejected);

finally

finally接受一个回调函数,在promise结束时,无论结果是fulfilled或者是rejected,都会执行该回调函数

1
2
3
4
5
finally = (callback) =>
this.then(
(value) => MyPromise.resolve(callback()).then(() => value),
(error) => MyPromise.resolve(callback()).then(() => MyPromise.reject(error))
);

完整代码

还有一些静态方法,resolverejectallreace等,基本实现了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
const PromiseStatusEnum = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
};

class MyPromise {
constructor(executor) {
this.status = PromiseStatusEnum.PENDING;
this.value = null;
this.reason = null;
this.resolveQueue = [];
this.rejectQueue = [];

const resolve = (value) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.FULFILLED;
this.value = value;

while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(value);
}
}
};

setTimeout(run);
};
const reject = (error) => {
const run = () => {
if (this.status === PromiseStatusEnum.PENDING) {
this.status = PromiseStatusEnum.REJECTED;
this.reason = error;

while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(error);
}
}
};

setTimeout(run);
};

try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}

then = (onFulfilled, onRejected) => {
return new MyPromise((resolve, reject) => {
const resolveFn = (value) => {
try {
const x = onFulfilled(value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};

const rejectFn = (error) => {
try {
const x = onRejected(error);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};

switch (this.status) {
case PromiseStatusEnum.PENDING:
this.resolveQueue.push(resolveFn);
this.rejectQueue.push(rejectFn);
break;
case PromiseStatusEnum.FULFILLED:
resolveFn(this.value);
break;
case PromiseStatusEnum.REJECTED:
rejectFn(this.reason);
break;
}
});
};

catch = (onRejected) => this.then(undefined, onRejected);

finally = (callback) =>
this.then(
(value) => MyPromise.resolve(callback()).then(() => value),
(error) => MyPromise.resolve(callback()).then(() => MyPromise.reject(error))
);

static resolve = (value) =>
new MyPromise((resolve, reject) =>
value instanceof MyPromise ? value : new MyPromise((resolve, reject) => resolve(value))
);

static reject = (error) => new MyPromise((resolve, reject) => reject(error));

// 静态all方法
static all = (promiseArr) => {
let count = 0;
let result = [];
return new MyPromise((resolve, reject) => {
if (!promiseArr.length) {
return resolve(result);
}
promiseArr.forEach((p, i) => {
MyPromise.resolve(p).then(
(value) => {
count++;
result[i] = value;
if (count === promiseArr.length) {
resolve(result);
}
},
(error) => {
reject(error);
}
);
});
});
};

// 静态race方法
static race = (promiseArr) =>
new MyPromise((resolve, reject) => {
promiseArr.forEach((p) => {
MyPromise.resolve(p).then(
(value) => {
resolve(value);
},
(error) => {
reject(error);
}
);
});
});
}

const promise1 = new MyPromise((resolve, reject) => {
console.log(1);
resolve(2);
console.log(3);
reject(4);
});

const promise2 = new MyPromise((resolve, reject) => {
reject(5);
});

console.log(6);

promise1.then(console.log, console.log);
promise2.then(console.log, console.log);

它对应的输出结果与之前一致

1
2
3
4
5
1
3
6
2
5