前言

嗯嗯嗯,等写完这个博客我就去复习期末。

实现过程

基本属性

对一个promise来说,我们需要有下面这些东西

  1. 对应三个状态的表示
  2. 当前promise的数据(也就是成功获得的数据或者失败的原因)
  3. 当前promise的状态
  4. 成功时要执行的回调函数列表
  5. 失败时要执行的回调函数列表

另外,为了使用属性私有化,我们要用一个立即执行函数来生成class,然后私有属性全部使用Symbol类型定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
return class {
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
}
}
})();

可以看到我们把状态,数据,两个回调列表存到了promise上,然后我们就可以逐步实现一个promise了

完成基础版本

先看看promise最基础的用法

1
2
3
let promise = new Promise((resolve, reject) => {
resolve("sena");
});

我们可以看到,在new Promise时,会传入一个函数,这个函数会立即执行,这个函数接收两个参数,分别是resolve和reject,这两个函数是定义好的,这个函数做了下面几件事

  1. 改变当前promise的状态
  2. 把传入的参数作为当前promise的数据保存起来
  3. 执行对应回调列表里里的回调函数

另外要注意的是,promise的状态是不可逆的,只能从pending变成resolved或者rejected,不能倒回去,所以我们要先做个判断再改变状态。

因为修改状态为成功和失败的逻辑都是一致的,所以我们可以把逻辑抽象为一个私有的工具函数

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 MyPromise = (function () {
// 上面已经有的重复代码
......
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
return class {
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue 新的 promise数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 把值传给回调函数执行
handler(newValue);
})
}
constructor(executor) {
.....
}
}
})();

好了,我们现在已经定义了一个工具函数updateStatus,但这个工具函数还有一点不好的地方,那就是then绑定的回调函数都是放到微任务中异步执行的,而我们这里是同步执行,在浏览器环境下,没有办法把回调函数放到微任务中,只能放到宏任务队列中异步执行,所以我们还要再更新一下updateStatus函数

因为把一个回调函数推入宏任务队列也可以抽象一下,所以我们写一个把回调函数推入任务队列的工具函数

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
const MyPromise = (function () {
......
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把任务添加到宏任务队列里的函数
const executeAsync = Symbol("executeAsync");
return class {
/**
* 把任务推入宏队列
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
......
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
constructor(executor) {
......
}
}
})();

然后我们在构造器中创建resolve和reject两个函数,传入executor即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}

这样使用时就可以接收到resolve和reject这两个函数,然后调用他们时,就可以改变promise的状态,然后就可以执行then中注册到回调列表中的函数了。

当然我们还需要做一点小收尾,因为如果传入的executor函数报错,会调用这个promise的reject方法,并把错误信息传入,所以我们需要加一个try catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}

然后看看我们现在已经写好的所有的代码

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
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把任务添加到宏任务队列里的函数
const executeAsync = Symbol("executeAsync");
return class {
/**
* 把任务推入宏队列
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}
}
})();

测试一下

1
2
3
let promise = new MyPromise((resolve, reject) => {
resolve("sena");
});

自然是没有反应的,因为没有then,没有注册回调函数,自然什么也不会发生

实现then

实现then也不难,根据规范,我们需要返回一个promise,因为返回的新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
const MyPromise = (function () {
......
// 创建串联的promise
const createLinkPromise = Symbol("createLinkPromise");
return class {
......
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {

});
}
then(onFulfilled, onRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
constructor(executor) {
......
}

}
})();

这里我们使用了一个新的工具函数createLinkPromise,用于返回一个新的promise,在这里我们会对then的绑定的回调函数进行处理。

实现单个then的绑定事件

then如果传入了回调函数,首先会判断当前promise的状态,如果已经是resolve或者reject状态,就把回调函数推入微任务队列中异步执行,如果还是pending状态,就把回调函数放到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
const MyPromise = (function () {
......
// 处理then中传入的回调
const settleHandle = Symbol("settleHandle");
return class {

......
/**
* 处理then传入的回调函数
* @param handler 回调函数
* @param immediatelyStatus 立即推入任务队列执行的状态
* @param queue 如果不立即执行,保存的队列
*/
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就处理then传入的回调了
this[settleHandle](resolve, RESOLVED, this[onFulfilledList]);
this[settleHandle](reject, REJECTED, this[onRejectedList]);
});
}
then(onFulfilled, onRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}



constructor(executor) {
......
}
}
})();

settleHandle就是用来处理回调函数的工具函数。

好了,现在我们可以给promise通过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
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
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把回调函数添加到任务队列里异步执行的函数
const executeAsync = Symbol("executeAsync");
// 创建串联的promise
const createLinkPromise = Symbol("createLinkPromise");
// 后续处理的通用函数
const settleHandle = Symbol("settleHandle");
return class {
/**
* 让回调函数异步执行
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
/**
* 处理then传入的回调函数
* @param handler 回调函数
* @param immediatelyStatus 立即推入任务队列执行的状态
* @param queue 如果不立即执行,保存的队列
*/
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就处理then传入的回调了
this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]);
this[settleHandle](onRejected, REJECTED, this[onRejectedList]);
});
}
then(onFulfilled, onRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}
}
})();

测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let promise = new MyPromise((resolve, reject) => {
setTimeout(function () {
resolve("sena");
}, 1000);
console.log("123"); // 输出123
});
promise.then(function (data) {
console.log(1, data); // 1s后输出1 "sena"
});
promise.then(function (data) {
console.log(2, data); // 1s后输出2 "sena"
});
promise.then(function (data) {
console.log(3, data); // 1s后输出3 "sena"
});

实现then的链式调用

因为then已经返回了一个promise,所以我们要做的事情比较简单,就是处理一下回调函数的返回值和处理回调函数抛出的error就就行了,我们先处理返回值,返回值有两种情况

下文的的下一个Promise代指createLinkPromise返回的promise

  1. 返回非promise,直接把返回值通过调用resolve()传给下一个promise,下一个promise把这个返回值传给then注册的回调函数,就实现了前一个then的返回值作为下个then的参数
  2. 返回promise,给返回的promise用then注册成功和失败的回调,在两个回调中分别调用能修改下一个promise的状态的resolve和reject方法,从而完成数据的传递和延时执行

我们看看之前写的代码

1
2
3
4
5
6
7
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就处理then传入的回调了
this[settleHandle](onFulfilled, RESOLVED, this[onFulfilledList]);
this[settleHandle](onRejected, REJECTED, this[onRejectedList]);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}

很明显,因为我们不知道当前promise的状态,所以我们不知道传入的回调函数声明时候执行,所以要接收返回值,就要做一些特殊处理。

处理的方法也很简单,在回调函数的外面包一层,在外层函数里接收回调函数的返回值就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就能处理then传入的回调了
// 在外面套一层
this[settleHandle]((data) => {
// 在这里就可以接收返回值,对返回值进行处理
let result = onFulfilled(data);
// 把返回值传给下个promise
resolve(result);
}, RESOLVED, this[onFulfilledList]);
this[settleHandle]((err) => {
let result = onRejected(err);
resolve(result);
}, REJECTED, this[onRejectedList]);
});
}

根据规范,如果then传入的onFulfilled函数执行时抛出了错误,就会调用reject函数,所以我们加个try catch在外面,由因为加try catch是重复的逻辑,所以我们可以再抽成一个工具函数execute。修改后的代码是这样的

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
const MyPromise = (function () {
......
// 执行then绑定的回调, 同时try catch
const execute = Symbol("execute");
return class {

......
/**
* 执行回调,获取返回值,如果报错就执行reject
* @param data 传给handler的数据
* @param handler 要在try catch中执行的回调函数
* @param resolve 下个promise的resolve
* @param reject 下个promise的reject
*/
[execute] (data, handler, resolve, reject) {
try {
const result = handler(data);
resolve(result);
}catch (e) {
reject(e);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就处理then传入的回调了
this[settleHandle]((data) => {
this[execute](data, onFulfilled, resolve, reject);
}, RESOLVED, this[onFulfilledList]);
this[settleHandle]((err) => {
this[execute](err, onRejected, resolve, reject);
}, REJECTED, this[onRejectedList]);
});
}

constructor(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
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
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把任务添加到任务队列里异步执行的函数
const executeAsync = Symbol("executeAsync");
// 创建串联的promise
const createLinkPromise = Symbol("createLinkPromise");
// 后续处理的通用函数
const settleHandle = Symbol("settleHandle");
// 执行then绑定的回调, 同时try catch
const execute = Symbol("execute");

return class {
/**
* 异步执行回调函数
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
/**
* 处理then传入的回调函数
* @param handler 回调函数
* @param immediatelyStatus 立即推入任务队列执行的状态
* @param queue 如果不立即执行,保存的队列
*/
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}
/**
* 执行回调,获取返回值,如果报错就执行reject
* @param data 传给handler的数据
* @param handler 要在try catch中执行的回调函数
* @param resolve 下个promise的resolve
* @param reject 下个promise的reject
*/
[execute] (data, handler, resolve, reject) {
try {
const result = handler(data);
// 如果返回值是promise
if (result instanceof MyPromise) {
// 用then注册回调
result.then((data) => {
// 把回调收到的数据转发给下个promise
resolve(data);
}, (err) => {
reject(err);
})
}else {
// 如果返回值不是promise,直接传递就行
resolve(result);
}
}catch (e) {
reject(e);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就能处理then传入的回调了
this[settleHandle]((data) => {
this[execute](data, onFulfilled, resolve, reject);
}, RESOLVED, this[onFulfilledList]);
this[settleHandle]((err) => {
this[execute](err, onRejected, resolve, reject);
}, REJECTED, this[onRejectedList]);
});
}
then(onFulfilled, onRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}
}
})();

好了,现在我们已经处理完了返回值不是promise的情况,让我们测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let promise = new MyPromise((resolve, reject) => {
setTimeout(function () {
resolve(1);
}, 1000);
}).then(function (data) {
console.log(data); // 1s后输出1
return data + 1;
}).then(function (data) {
console.log(data); // 1s后输出2
return data + 1;
}).then(function (data) {
console.log(data); // 1s后输出3
return data + 1;
});

现在我们来处理返回值是promise的情况,处理的方法原理也不难,给返回的promise注册回调,然后在回调里修改下一个promise的状态就行了

PS:下一个promise指的是createLinkPromise返回的promise

这里直接修改execute函数就行了

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
/**
* 执行回调,获取返回值,如果报错就执行reject
* @param data 传给handler的数据
* @param handler 要在try catch中执行的回调函数
* @param resolve 下个promise的resolve
* @param reject 下个promise的reject
*/
[execute] (data, handler, resolve, reject) {
try {
const result = handler(data);
// 如果返回值是promise
if (result instanceof MyPromise) {
// 用then注册回调
result.then((data) => {
// 把回调收到的数据转发给下个promise
resolve(data);
}, (err) => {
reject(err);
})
}else {
// 如果返回值不是promise,直接传递就行
resolve(result);
}
}catch (e) {
reject(e);
}
}

好了,返回值是promise的情况也处理好了,来测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let promise = new MyPromise((resolve, reject) => {
setTimeout(function () {
resolve("sena");
}, 1000);
console.log("sion");
}).then((data) => {
console.log("1",data);
return data;
}).then((data) => {
console.log("2",data);
return new MyPromise((resolve) => {
setTimeout(() => {
resolve("sakura");
}, 2000)
});
}).then((data) => {
console.log("3",data);
return data;
});
console.log("yyy");

输出情况,看起来是正常的

1
2
3
4
5
// sion
// yyy
// 1s后输出: 1 sena
// 1s后输出: 2 sena
// 2s后输出: 3 sakura

其实到这里,一个promise大致就完成了,接下来就是一些细节处理了

处理空then

如果中间的某个then没有传入回调,虽然我没在规范中找到这种情况(可能是我看漏了),但是在我测试官方promise时回调函数的执行不会中断,所以我们要做些处理

处理的方法也比较简单,如果onFulfilled, onRejected没有传入,使用默认的onFulfilled, onRejected函数,把数据转发就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const MyPromise = (function () {
......
// 默认的OnFulfilled
const defaultOnFulfilled = function (data) {return data};
// 默认的OnRejected
const defaultOnRejected = function (err) {throw new Error(err)};
return class {

// 给onFulfilled, onRejected 默认值
then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
constructor(executor) {
......
}
}
})();

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let promise = new MyPromise((resolve, reject) => {
setTimeout(function () {
resolve("sena");
}, 1000);
console.log("sion");
}).then((data) => {
console.log("1",data);
return data;
}).then((data) => {
console.log("2",data);
return new MyPromise((resolve) => {
setTimeout(() => {
resolve("sakura");
}, 1000)
});
}).then().then((data) => {
console.log("3",data);
return data;
});
console.log("yyy");

输出情况

1
2
3
4
5
// sion
// yyy
// 1s后输出: 1 sena
// 1s后输出: 2 sena
// 2s后输出: 3 sakura 如果没有处理空then,不会出现这个

完善promise的其他函数

catch和finally函数

catch其实是特殊的then函数,简单来说就是没有onFulfilled的then函数。

finally也是特殊的then函数,就是无论什么状态都执行相同的回调

所以可以直接调用then来实现

1
2
3
4
5
6
catch(onRejected) {
return this.then(defaultOnFulfilled, onRejected);
}
finally(handler) {
return this.then(handler, handler);
}

all函数

all函数接收一个promise数组,返回一个promise,只有传入的数据中的所有promise都处于resolved状态后,返回的promise才会处于resolved状态,有任何一个promise处于reject状态后,返回的promise就会处于reject状态

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
static all(promises) {
// promise数组的长度
let length = promises.length;
// 创建用于保存结果的数组
let resultArr = new Array(length);
// 已经处于resolved状态的promise的个数
let count = 0;
return new MyPromise((resolve, reject) => {
promises.map((promise, index) => {
// 给每个promise都注册回调
promise.then((data) => {
// 处于resolve状态后, 把count加1
count ++;
// 保存数据到数组中
resultArr[index] = data;
// 如果全部处于resolved状态
if (count >= length) {
// 就把返回的promise变成resolved状态
resolve(resultArr);
}
}, (err) => {
// 有任一个处于rejected状态
// 就把返回的promise置为rejected状态
reject(err);
})
})
})
}

race函数

race函数接收一个promise数组,返回一个promise,返回的promise的状态会与promise数组中的第一个修改状态的项同步修改成相同的值

1
2
3
4
5
6
7
8
9
10
11
12
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// 任意一个修改状态后就修改返回的promise的状态
promise.then((data) => {
resolve(data);
}, (err) => {
reject(err);
})
})
});
}

resolve函数

resolve函数返回一个已经处于resolved状态的promise,接收要传给回调函数的data

1
2
3
4
5
6
static resolve(data) {
if (data instanceof MyPromise) return data;
return new MyPromise((resolve) => {
resolve(data);
})
}

reject函数

reject函数返回一个已经处于reject状态的promise,接收要传给回调函数的data

1
2
3
4
5
6
static reject(err) {
if (data instanceof MyPromise) return err;
return new MyPromise((resolve, reject) => {
reject(err);
})
}

好了,到这里所有的代码就暂时实现完了,当然我后期会再加些方法上去,不过那是以后的事了,最后放一波完整的代码

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把任务添加到任务队列里异步执行的函数
const executeAsync = Symbol("executeAsync");
// 创建串联的promise
const createLinkPromise = Symbol("createLinkPromise");
// 后续处理的通用函数
const settleHandle = Symbol("settleHandle");
// 执行then绑定的回调, 同时try catch
const execute = Symbol("execute");
// 默认的OnFulfilled
const defaultOnFulfilled = function (data) {return data};
// 默认的OnRejected
const defaultOnRejected = function (err) {throw new Error(err)};
return class {
/**
* 异步执行回调函数
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
/**
* 处理then传入的回调函数
* @param handler 回调函数
* @param immediatelyStatus 立即推入任务队列执行的状态
* @param queue 如果不立即执行,保存的队列
*/
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}
/**
* 执行回调,获取返回值,如果报错就执行reject
* @param data 传给handler的数据
* @param handler 要在try catch中执行的回调函数
* @param resolve 下个promise的resolve
* @param reject 下个promise的reject
*/
[execute] (data, handler, resolve, reject) {
try {
const result = handler(data);
// 如果返回值是promise
if (result instanceof MyPromise) {
// 用then注册回调
result.then((data) => {
// 把回调收到的数据转发给下个promise
resolve(data);
}, (err) => {
reject(err);
})
}else {
// 如果返回值不是promise,直接传递就行
resolve(result);
}
}catch (e) {
reject(e);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就能处理then传入的回调了
this[settleHandle]((data) => {
this[execute](data, onFulfilled, resolve, reject);
}, RESOLVED, this[onFulfilledList]);
this[settleHandle]((err) => {
this[execute](err, onRejected, resolve, reject);
}, REJECTED, this[onRejectedList]);
});
}
then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
catch(onRejected) {
return this.then(defaultOnFulfilled, onRejected);
}
finally(handler) {
return this.then(handler, handler);
}
constructor(executor) {
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}
static all(promises) {
// promise数组的长度
let length = promises.length;
// 创建用于保存结果的数组
let resultArr = new Array(length);
// 已经处于resolved状态的promise的个数
let count = 0;
return new MyPromise((resolve, reject) => {
promises.map((promise, index) => {
// 给每个promise都注册回调
promise.then((data) => {
// 处于resolve状态后, 把count加1
count ++;
// 保存数据到数组中
resultArr[index] = data;
// 如果全部处于resolved状态
if (count >= length) {
// 就把返回的promise变成resolved状态
resolve(resultArr);
}
}, (err) => {
// 有任一个处于rejected状态
// 就把返回的promise置为rejected状态
reject(err);
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// 任意一个修改状态后就修改返回的promise的状态
promise.then((data) => {
resolve(data);
}, (err) => {
reject(err);
})
})
});
}
static resolve(data) {
if (data instanceof MyPromise) return data;
return new MyPromise((resolve) => {
resolve(data);
})
}
static reject(err) {
if (data instanceof MyPromise) return err;
return new MyPromise((resolve, reject) => {
reject(err);
})
}
}
})();

感谢你的阅读