自己实现一个Promise

Posted by 黄成都 on 2020-04-22
Words 3.1k and Reading Time 14 Minutes
Viewed Times

概述

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。Promise对象有以下两个特点。

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

ES5方式实现

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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// Promise的三个状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

/**
* 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
* 2. executor 接受两个参数,分别是 resolve 和 reject
* 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
* 4. promise 的状态一旦确认,就不会再改变
* 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled,
* 和 promise 失败的回调 onRejected
* 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
* 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
* 如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
* 7. then 的参数 onFulfilled 和 onRejected 可以缺省
* 8. promise 可以then多次,promise 的then 方法返回一个 promise
* 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
* 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
* 11.如果 then 返回的是一个promise,那么会等这个promise执行完,promise如果成功,
* 就走下一个then的成功,如果失败,就走下一个then的失败
*/
function Promise(executor) {
let self = this;
// 默认状态
self.status = PENDING;
// 状态从pending变为reject后面的then回调列表
self.onFulfilled = [];
// 状态从pending变为reject后面的then回调列表
self.onRejected = [];

function resolve(value) {
self.status = FULFILLED;
//Promise规范 resolve状态必须要有value
self.value = value;
//根据添加顺序调用后面then的resolve回调
self.onFulfilled.forEach(fn => fn());
}

function reject(reason) {
if (self.status === PENDING) {
self.status = REJECTED;
// Promise规范 reject状态必须要有reason
self.reason = reason;
//根据添加熟悉怒调用后面then的reject回调
self.onRejected.forEach(fn => fn());
}
}

try {
// Promise规范,执行器要立即执行
executor(resolve, reject);
} catch (error) {
resolve(error);
}
}

Promise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

let self = this;
// Promise规范,then必须返回一个Promise对象
let promise2 = new Promise((resolve, reject) => {
if (self.status === FULFILLED) {
// 通过setTimeout来模拟Promise规范,
setTimeout(() => {
try {
let x = onFulfilled(self.value);
// 主要目的处理resolve的不同返回类型的数据,如function,Promise等
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (self.status === REJECTED) {
// 通过setTimeout来模拟Promise规范,
setTimeout(() => {
try {
let x = onRejected(self.reason);
// 主要目的处理resolve的不同返回类型的数据,如function,Promise等
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (self.status === PENDING) {
// 把then的回调加入resolve状态
self.onFulfilled.push(() => {
// 通过setTimeout来模拟Promise规范,
setTimeout(() => {
try {
let x = onFulfilled(self.value);
// 主要目的处理resolve的不同返回类型的数据,如function,Promise等
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
// 把then的回调加入reject状态
self.onRejected.push(() => {
// 通过setTimeout来模拟Promise规范,
setTimeout(() => {
try {
let x = onRejected(self.reason);
// 主要目的处理resolve的不同返回类型的数据,如function,Promise等
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return promise2;
}

// 主要是处理then返回的不同对象
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
reject(new TypeError('多次调用了'));
}
// Promise是一个有then方法的对象或者是函数, 这里主要处理resolve的回调参数是一个Promise对象
if (x && typeof x === 'object' || typeof x === 'function') {
let used;
try {
// 如果是一个对象,并且有then方法,则递归调用
let then = x.then;
if (typeof then === 'function') {
// x 是有then方法的对象
// y 表示then方法对应的resolve
// r 表示then方法对应的reject
then.call(x, (y) => {
if (used) {
return;
}
used = true;
// 递归调用
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (used) {
return;
}
used = true;
// reject状态调用
reject(r);
});
} else {
if (used) {
return;
}
used = true;
// 普通方法直接resolve
resolve(x);
}
} catch (error) {
if (used) {
return;
}
used = true;
// 出错了,reject状态调用
reject(error);
}
} else {
// 基本类型,resolve状态调用
resolve(x);
}
}


Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}

// Promise.resolve(value) 返回一个以给定值解析后的Promise 对象.
// 如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
// 如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象。
// 其他情况,直接返回以该值为成功状态的promise对象。
Promise.resolve = function (param) {
if (param instanceof Promise) {
return param;
}
return new Promise((resolve, reject) => {
if (param && param.then && typeof param.then === 'function') {
setTimeout(() => {
param.then(resolve, reject);
});
} else {
resolve(param);
}
});
}

// Promise.reject方法和Promise.resolve不同,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}

// Promise.prototype.catch 用于指定出错时的回调,是特殊的then方法,catch之后,可以继续 .then
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}

// 不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.
Promise.prototype.finally = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
}, (err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
});
}

// 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
// 如果传入的参数不包含任何 promise,则返回一个异步完成.
// promises 中所有的promise都promise都“完成”时或参数中不包含 promise 时回调完成。
// 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
// 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
Promise.all = function (promises) {
return new Promise((resolve,reject) => {
let index = 0;
let result = []
if (promises.length === 0) {
resolve(result);
}

function processValue(i, value) {
result[i] = value;
if (++index === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((data) => {
processValue(i, data);
}, (reason) => {
reject(reason);
return;
});
}
});
}

// Promise.race函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
// 如果传的参数数组是空,则返回的 promise 将永远等待。
// Promise.race函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
// 如果传的参数数组是空,则返回的 promise 将永远等待。
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return;
}
let hasUsed = false;
for (let index = 0; index < promises.length; index++) {
Promise.resolve(promises[index]).then(
(value) => {
if (!hasUsed) {
hasUsed = true;
resolve(value);
}
return;
},
(reason) => {
if (!hasUsed) {
hasUsed = true;
reject(reason);
}
return;
}
);
}
});
};

module.exports = Promise;

ES6实现

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
//Promise/A+规定的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class Promise {
// 构造方法接收一个回调
constructor(executor) {
this._status = PENDING; // Promise的状态
this._value = undefined; //存储then回调return的值
this._resolveQueue = []; //成功队列,resolve时触发
this._rejectQueue = []; //失败队列,reject时触发

// 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向,否则找不到this._resolveQueue
let _resolve = (val) => {
const run = () => {
// 对应规范:状态只能由pending到fulfilled或者rejected
if (this._status !== PENDING) {
return;
}
this._status = FULFILLED; //状态变更
this._value = val; //存储当前value

// 把then回调放在一个队列来存储,是为了实现对应的规范"then 方法可以被同一个promise调用多次"
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift();
callback && callback(val);
}
};
// 把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况
setTimeout(run);
};

let _reject = (val) => {
const run = () => {
if (this._status !== PENDING) {
return;
}
this._status = REJECTED;
this._value = val;

while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback && callback(val);
}
};
setTimeout(run);
};

// new Promise()时立即执行executor,并传入resolve和reject
executor(_resolve, _reject);
}

// then方法,接收一个成功的回调和一个失败的回调
then(resolveFn, rejectFn) {
// 根据规范, 如果then的参数不是function, 则我们需要忽略它,让链式调用继续往下执行
resolveFn = typeof resolveFn === "function" ? resolveFn : (value) => value;
rejectFn =
typeof rejectFn === "function"
? rejectFn
: (reason) => {
throw reason;
};

// 根据规范 then返回一个新的Promise对象
const newPromise = new Promise((resolve, reject) => {
// 把resolve重新包装一下, 再push进resolve的执行队列,这是为了能够获取回调的返回值进行分类处理
const fulfilledFn = (value) => {
try {
// 执行第一个(当前的)Promise的成功回调,并且获取返回值
let x = resolveFn(value);
// 分类讨论返回值,如果是Promise,那么等待Promise的状态更改,否则直接resolve
x instanceof Promise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};

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

switch (this._status) {
// 如果状态为pending时, 把then回调push进resolve/reject的执行队列,等待执行
case PENDING:
this._resolveQueue.push(fulfilledFn);
this._rejectQueue.push(rejectedFn);
break;
// 当前状态已经是resolve/reject的时候,直接执行then回调
case FULFILLED:
setTimeout(() => {
fulfilledFn(this._value);
});
break;
case REJECTED:
setTimeout(() => {
rejectedFn(this._value);
});
break;
default:
break;
}
});

return newPromise;
}
}

module.exports = Promise;

验证Promise

  • 安装测试脚本

    1
    npm install -g promises-aplus-tests
  • 执行对应的测试用例

    1
    promises-aplus-tests promise.js

目前测试的结果是。ES5版本完整通过测试,ES6版本还没有完全通过测试。

另外可以借助如下工具来看ES6转换为ES5的相关实现

img

测试

首要要引入

1
const Promise = require("./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
const TestPromise = useCallback(() => {
console.warn("====Promise Test===");
new Promise((resolve, reject) => {
resolve("123");
})
.then(
i => {
console.warn("i===>" + i);
return "i";
},
j => {
console.warn("j===>" + j);
return "j";
}
)
.then(
i2 => {
console.warn("i2=====>" + i2);
throw new Error("i2 throw error");
},
j2 => {
console.warn("j2=====>" + j2);
return "j2";
}
)
.then(
i3 => {
console.warn("i3=====>" + i3);
},
j3 => {
console.warn("j3=====>" + j3.message);
}
);
}, []);

结果

img

Promise all测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const TestPromiseAll = useCallback(() => {
console.warn("====PromiseAll Test===");
var promise1 = new Promise((resolve, reject) => {
resolve(3);
});
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3]).then(
values => {
console.log("values=======>" + values); //[3, 42, 'foo']
},
err => {
console.warn(err);
}
);
}, []);

结果

img

参考资料


...

...

00:00
00:00