31.Promise-深入Promise原理
# 0.前言
本节博客代码仓库 (opens new window)
本篇博客一开始想的标题是《从零基于 PromiseA+
实现 Promise
源码》,但是这种文章写出来,就是一堆 Promise
源码的底层实现,不利于对知识的分解与重构。
# 1.实现 MVP 版本的代码
假设不存在 Promise
这个概念,我们就是希望实现一个状态管理类,支持如下功能:
- 实现一个类库,该类有三个状态:
pending | fulfilled | rejected
- 通过接受一个
callback
函数实例化这个类,即new Promise(callback)
- 主体
callback
回调函数,需暴露出两个改变状态的函数。(resolve,reject)=>{}
。- 当调用
resolve()
函数时,将类的状态修改为fulfilled
。 - 当调用
reject()
函数时,将类的状态修改为rejected
。
- 当调用
- 使用
.then
可根据状态触发不同的分支。当pending
状态时,不会继续执行。当fulfilled
或者rejected
时代码可以继续流通。
话不多说啊,开干,本案例的写法基于 TDD
测试驱动开发,先完成测试案例,再一步一步实现。这种开发的好处是:
- 测试案例本身就是一份非常完善的文档。
- 分步实现功能,不用一开始就把所有细节都考虑的完善,在一次次迭代中完善案例。
首先,先基于 vitest
搭建基础测试工程,具体过程可见《vitest 测试框架环境搭建》 这篇博客。
将核心逻辑编写在 src/promiseCore.ts
文件中,测试代码 src/promiseCore.test.ts
文件。
基础测试代码:
import { describe, expect, it, vi } from "vitest";
import { PromiseCore as Promise } from "./promiseCore";
/* 如果所有用例都并发执行的话,可以使用 describe.concurrent */
describe("Promise", () => {
it("1.保证是一个类", () => {
expect(Promise).toBeTypeOf("function");
});
it("2.接受一个 `callback` 函数可以实例化这个类", () => {
expect(() => new Promise()).toThrowError("请输入函数");
/* @ts-ignore */
expect(() => new Promise(1)).toThrowError("请输入函数");
/* @ts-ignore */
expect(() => new Promise(true)).toThrowError("请输入函数");
/* new Promise(fn) 中的 fn 立即执行 */
const fn = vi.fn();
new Promise(fn);
expect(fn).toHaveBeenCalled();
});
it("3.callback 接受 resolve 和 reject 两个函数", () => {
new Promise((resolve, reject) => {
expect(resolve).toBeTypeOf("function");
expect(reject).toBeTypeOf("function");
});
});
});
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
基础实现如下:
class PromiseCore {
state: "pending" | "fulfilled" | "rejected" = "pending";
/* 接受案例完成实例化构建 */
constructor(fn: Function) {
if (typeof fn !== "function") {
throw new Error("请输入函数");
}
/* 暴露出两个函数,先用空函数实现 */
fn(this.resolve.bind(this), this.reject.bind(this));
}
/* 原形方法 */
resolve() {
/* 待完善 */
}
reject() {
/* 待完善 */
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上面的写法虽然简单,但是过前面的测试,接下来要实现 .then
以及修改状态这部分功能了。
import { describe, expect, it, vi } from "vitest";
import { PromiseCore as Promise } from "./promiseCore";
/* 如果所有用例都并发执行的话,可以使用 describe.concurrent */
describe("Promise", () => {
it("4.then 支持两个函数,也可不传", () => {
new Promise((resolve, reject) => {}).then(null, null);
new Promise((resolve, reject) => {}).then(
() => {},
() => {},
);
});
it("4.1 promise.then(success) 中的 success 会在 resolve 被调用的时候执行", () =>
new Promise((done) => {
/* vitest 中的 done 写法:https://cn.vitest.dev/guide/migration.html */
const success = vi.fn();
const promise = new Promise((resolve, reject) => {
expect(success).not.toHaveBeenCalled();
setTimeout(() => {
resolve();
}, 10);
});
expect(promise.state).toMatchInlineSnapshot('"pending"');
setTimeout(() => {
expect(success).toHaveBeenCalled();
expect(promise.state).toMatchInlineSnapshot('"fulfilled"');
done();
}, 20);
promise.then(success);
}));
it("4.2 promise.then(null,fail) 中的 fail 会在 reject 被调用的时候执行", () =>
new Promise((done) => {
const fail = vi.fn();
const promise = new Promise((resolve, reject) => {
expect(fail).not.toHaveBeenCalled();
setTimeout(() => {
reject();
}, 10);
});
expect(promise.state).toMatchInlineSnapshot('"pending"');
setTimeout(() => {
expect(fail).toHaveBeenCalled();
expect(promise.state).toMatchInlineSnapshot('"rejected"');
done();
}, 20);
promise.then(null, fail);
}));
});
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
通过上面的测试案例可知,.then
中的函数会被 resolve
或者 reject
调用。并且
.then
是同步执行代码。.resolve
有可能被包裹在一个异步执行环境中,至于什么时候触发不清楚。
因此这里需要做的就是:先将 .then
中的结果缓存起来,在resolve
调用的时候再从变量中取出即可。
class PromiseCore {
state: "pending" | "fulfilled" | "rejected" = "pending";
succcessCallback: Function = undefined;
failCallback: Function = undefined;
constructor(fn: Function) {
if (typeof fn !== "function") {
throw new Error("请输入函数");
}
fn(this.resolve.bind(this), this.reject.bind(this));
}
/* 原形方法 */
resolve() {
if (this.state !== "pending") return;
this.state = "fulfilled";
/* @todo: 这里先用 setTimeout 触发宏任务过程 */
setTimeout(() => {
this.successCallback();
});
}
reject() {
if (this.state !== "pending") return;
this.state = "rejected";
/* @todo: 这里先用 setTimeout 触发宏任务过程 */
setTimeout(() => {
this.failCallback();
});
}
/* then 的核心就是将对应的函数缓存在 successCallback 或者 failCallback 函数中 */
then(succeed?: CallBackType, fail?: CallBackType) {
if (typeof succeed === "function") {
successCallback = succeed;
}
if (typeof fail === "function") {
failCallback = fail;
}
}
}
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
这里有可能会有一个问题:resolve
或 reject
为啥在触发的时候需要通过 setTimeout
包裹一层呢?
主要是考虑到下面的案例:
new Promise((resolve, reject) => {
resolve("成功");
}).then((x) => {
console.log(x);
});
2
3
4
5
这里 resolve
并不是在一个异步环境中,但是执行逻辑是:
- 将
(x) => console.log(x)
保存到successCallback
函数到上。 - 这样以后才可以在
resolve
触发时获取到successCallback
不会为undefined
。
这里 setTimeout
的目的就是为了等一下 then
函数的执行,调换resolve
和 then
函数实际执行次序,现在这里先用简单的 setTimeout
宏任务处理,后续会替换的。
自此,一个非常简易的版本的 Promise
已经实现了,是不是不敢相信,Promise
的实现是如此的简单。虽然这种实现离一个可用的符合 PromiseA+
的 Promise
还差的很远。但是至少实现一个类状态控制器。进一步思考,是不是可以将这种编程范式扩展至其余的所需具备状态控制的领域呢?
# 2. 支持链式调用及结果传递
从本节开始完善 Promise
,尽量往 PromiseA+
规范上靠。
上述已实现状态控制,但是缺少如下核心功能:
promise.then
支持多次回调存储。.then
返回的是一个新的promise
(状态为pending
)promise(resolve => resolve("xxx")).then(x => console.log(x))
支持将结果传递出去。
由于其实现过程还是较为复杂的,先从支持多次回调存储功能出发:
it("【中】2.2.6 支持链式 then 的写法,要求调用顺序遵循书写顺序", () =>
new Promise((done) => {
const promise = new Promise((resolve) => {
resolve();
});
const callbacks = [vi.fn(), vi.fn(), vi.fn()];
promise.then(callbacks[0]);
promise.then(callbacks[1]);
promise.then(callbacks[2]);
setTimeout(() => {
expect(callbacks[0]).toHaveBeenCalled();
expect(callbacks[1]).toHaveBeenCalled();
expect(callbacks[2]).toHaveBeenCalled();
/* ChatGPT 给出的方案:如下结果分别为 [7][8][9] */
console.log(callbacks[0].mock.invocationCallOrder);
console.log(callbacks[1].mock.invocationCallOrder);
console.log(callbacks[2].mock.invocationCallOrder);
/* 0 < 1 */
expect(callbacks[0].mock.invocationCallOrder[0]).toBeLessThan(
callbacks[1].mock.invocationCallOrder[0],
);
/* 1 < 2 */
expect(callbacks[1].mock.invocationCallOrder[0]).toBeLessThan(
callbacks[2].mock.invocationCallOrder[0],
);
done();
});
}));
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
如果希望 .then
支持多次回调的话,原先的变量 successCallback
或者 failCallback
就显得不够用了,需要升级为回调数组,resolve()
时遍历 callback
数组。
class PromiseCore {
/* 其中 callbacks 对应如下:
successCallBack <= callback[0]
failCallBack <= callback[1]
.then 的回调 <= callback[2] 【暂未实现:后续将 nextPromise 缓存在此】
*/
callbacks: [CallBackType, CallBackType, myPromise][] = [];
/* 原型方法 */
resolve(result) {
if (this.state !== "pending") return;
this.state = "fulfilled";
nextTick(() => {
this.callbacks.forEach((handle) => {
const success = handle[0];
if (typeof success === "function") {
const x = success(result);
}
});
});
}
reject(reason) {
if (this.state !== "pending") return;
this.state = "rejected";
nextTick(() => {
this.callbacks.forEach((handle) => {
const fail = handle[1];
if (typeof fail === "function") {
fail(reason);
}
});
});
}
}
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
返回的是一个 Promise
,且状态为 pending
如果是一般的类函数实现链式调用的话,有个技巧是直接返回自身即可,但这种做法在此案例中并不适用。原因在于要求返回的类状态必须有要求,为 pending
状态。
因此 .then
的返回结果是一个全新具有 pending
状态的 Promise
,代码示例如下:
class PromiseCore {
then(succeed?: CallBackType, fail?: CallBackType) {
const handle = [] as unknown as [CallBackType, CallBackType, any];
if (typeof succeed === "function") {
handle[0] = succeed;
}
if (typeof fail === "function") {
handle[1] = fail;
}
/* 错误的写法 */
- return this;
/* 返回一个新的 Promise */
+ return new PromiseCore(() => {});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
第三条规则:将结果参数进行链式传递,我认为这条规则是 Promise
编码中最难实现的规则之一。
其实它的 MVP
实现也是可以通过简单的临时变量实现的,构造 nextPromise
变量,在 then
函数返回时对该变量赋值。当 resolve
时,将结果传递给 nextPromise.resolve()
函数中。
class PromiseCore {
+ nextPromise = undefined;
then(success?,fail?){
.....
/* 1. 将 新的 Promise 缓存起来 */
+ nextPromise = new PromiseCore(()=>{});
return nextPromise;
}
resolve(result){
if (this.state !== "pending") return;
this.state = "fulfilled";
setTimeout(() => {
this.callbacks.forEach((handle) => {
const success = handle[0];
if (typeof success === "function") {
/* 2. 缓存 x 计算的结果 */
const x = success(result);
/* 3. 将计算的结果传递给 nextPtomise.resolve(x) */
+ nextPromise.resolve(x);
}
});
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PS:为了后续编码考虑,可将 nextPromise
作为 callback
的第三个参数一起缓存。
自此,Promise
的核心功能已基本实现,如果用户不做什么骚操作的话是大致是没问题的,但是作为函数库来说,必须对所有可能犯的错进行避免,提升函数鲁棒性。
# 3. 注意:函数库调用时设置严格模式
通过上面的案例,我们已经掌握 Promise
中的一个核心逻辑是 resolve
触发的是在 .then()
中缓存的函数。函数触发时可以通过 this
访问到 Promise
实例,这一点最好是被限制住,始终保证为严格模式会比较好。
测试案例:
注:以下在
.then
中是function
写法,如果是箭头函数就不存在这个问题了 。
it("【难】2.2.5 onFulfilled和onRejected 被当做函数调用时,this 指向 undefined", () => {
const promise = new Promise((resolve) => {
resolve();
});
/* function 写法 */
promise.then(function() {
/* 注意此时应为严格模式 */
console.log("this", this);
expect(this).toBeUndefined();
});
});
2
3
4
5
6
7
8
9
10
11
12
13
希望测试的是 .then
中访问 this
指针时需要重置为 undefined
保证严格模式。
实现时,只需要将 resolve
或者 reject
触发时需要通过 bind
重新指向 undefined
即可。
class PromiseCore {
/* 原型方法 */
resolve(result) {
if (this.state !== "pending") return;
this.state = "fulfilled";
nextTick(() => {
this.callbacks.forEach((handle) => {
const success = handle[0];
if (typeof success === "function") {
- const x = success(result);
+ const x = success.call(undefined, result);
handle[2].resolveWith(x);
}
});
});
}
reject(reason) {
if (this.state !== "pending") return;
this.state = "rejected";
nextTick(() => {
this.callbacks.forEach((handle) => {
const fail = handle[1];
if (typeof fail === "function") {
- fail(reason);
+ fail.call(undefined, reason);
}
});
});
}
}
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
# 4. 处理异常回调结果
这部分主要对应于 2.2.7.1
规则,前面的 resolve()
仅支持普通类型的数据,通过新增 resolveWith
处理更多的参数类型。
将原先的参数回调改造如下:
class PromiseCore {
/* 使用 function 定义规则 */
+ resolveWith(){
+ // 此处 this 指向 nextPromise
+ }
/* 原型方法 */
resolve(result) {
if (this.state !== "pending") return;
this.state = "fulfilled";
nextTick(() => {
this.callbacks.forEach((handle) => {
const success = handle[0];
if (typeof success === "function") {
/* 保证调用时为严格模式 */
const x = success.call(undefined, result);
- nextPromise.resolve(x);
+ nextPromise.resolveWith(x);
}
});
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
基于 PromiseA+
的规则约定如下:
- 如果
resolve
接受的是nextPromise
自身,会引起循环处理,需要将此错误reject
。 - 如果
resolve
接受的是一个Promise
实例,则使用then
取出结果后透传给resovle
或者reject
参数。 - 如果
resolve
接受的是一个thenable
对象,和上述的Promise
不同之处在于,Promise.then()
取出的一定是个普通类型的结果,而thenable.then()
有可能仍是一个Promise/thenable/普通数据类型
。因此需要使用resolveWith()
进行递归处理。 - 如果
resolve
接受的是一个普通类型,就直接调用resolve(res)
函数 。
it("2.2.7.1.2 success 的返回值是一个 Promise 实例", () => {
const promise1 = new Promise((resolve) => resolve());
/* .then 中返回的是一个 Promise 类型*/
const promise2 = promise1.then(
() => new Promise((resolve) => resolve("成功")),
);
/* 此时 x 为 new Promise((resolve) => resolve("成功")
直接 x.then 后取出结果,并传递给 resolveWith 函数中的 this.resolve() 即可。
*/
promise2.then((res) => {
console.log("res", res);
expect(res).toMatchInlineSnapshot('"成功"');
});
});
it("【需非常注意】2.2.7.1.2 success 的返回值是一个 thenable 对象, 成功调用", () => {
const promise1 = new Promise((resolve) => resolve());
/* .then 中返回的是一个 Promise 类型*/
const promise2 = promise1.then(() => {
return {
then: (resolve, reject) => {
resolve("成功");
},
};
});
promise2.then((res) => {
console.log("res", res);
expect(res).toMatchInlineSnapshot('"成功"');
});
});
it("【需非常注意】2.2.7.1.2 success 的返回值是一个 thenable 对象, 失败调用", () => {
const promise1 = new Promise((resolve) => resolve());
/* .then 中返回的是一个 Promise 类型*/
const promise2 = promise1.then(() => {
return {
then: (resolve, reject) => {
reject("失败");
},
};
});
promise2.then(null, (reason) => {
console.log("res", reason);
expect(reason).toMatchInlineSnapshot('"失败"');
});
});
it("【需非常注意】2.2.7.1.2 success 的返回值不能是 promise 自身索引", () =>
new Promise((done) => {
const promise1 = new Promise((resolve) => resolve());
/* 使用 promise1 的 then 返回一个新引用地址的 promise2 */
/* 不允许处理自身的引用地址 */
const promise2 = promise1.then(() => promise2);
promise2.then(null, (reason) => {
expect(reason).toMatchInlineSnapshot("[TypeError: 不允许返回自身引用]");
done();
});
}));
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
对应的实现如下:
class PromiseCore {
/* 此时为 Promise2 环境 */
resolveWith(x) {
/* 如果 then 返回的是引用自身,则报错 */
if (this === x) {
this.reject(new TypeError("不允许返回自身引用"));
} else if (x instanceof PromiseCore) {
/* 如果是一个 Promise, 需要通过 then 获取值 */
x.then(
(result) => {
this.resolve(result);
},
(reason) => {
this.reject(reason);
},
);
} else if (x instanceof Object) {
/* 如果 x 是 thenable */
let then;
try {
then = x.then;
} catch (error) {
this.reject(error);
}
if (then instanceof Function) {
try {
then(
(y) => {
/* 特别注意:x 是 thenable 和 promise 最大的不同是,x.then 的回调结果
前者执行后还有可能是 自身引用/promise/thenable/普通类型; 而后者只有可能是普通类型。
因此这里千万注意要使用 this.resolveWith(y)
*/
this.resolveWith(y);
},
(r) => {
this.reject(r);
},
);
} catch (error) {
this.reject(error);
}
}
} else {
/* 如果 x 是一个普通类型 */
/* 通过 this.resolve 将结果传递给 then 后的回调结果 */
this.resolve(x);
}
}
}
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
# 5.微任务 PolyFill 方案
此前代码都是使用 setTimeout
将 then
和 resolve/reject
执行次序颠倒,但是这对于代码中存在微任务代码时可能会出现一些问题。
因此需要保证 PromiseCore
改造为微任务,对于不同的环境微任务的处理方案有所不同:
- 对于
Node
执行环境,直接使用process.nextTick()
即可。 - 对于
browser
执行环境,使用MutationObserver
实现Polyfill
功能。- 创建一个虚拟的节点:
TextNode
- 开启
Observer
监听节点的改变。 - 改变状态
TextNode.data = count + 1
- 创建一个虚拟的节点:
/* 添加 polyfill */
function nextTick(fn) {
/* Node 环境下使用 process.nextTick 模拟 */
/* @ts-ignore */
if (process !== undefined && typeof process.nextTick === "function") {
/* @ts-ignore */
return process.nextTick(fn);
}
/* Vue源码:浏览器环境使用 MutationObserver */
let counter = 1;
const observer = new MutationObserver(fn);
/* 1. 创建一个虚拟的文本节点 */
const textNode = document.createTextNode(counter + "");
/* 2. 开启 Mutation 去监听 TextNode 这个 DOM 元素 */
observer.observe(textNode, {
characterData: true,
});
/* 3. 触发 TextNode 数据属性的改变 */
counter += 1;
textNode.data = counter + "";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
通过查阅 【W3C 官网】 (opens new window) 可知 MutationObserver
是一个微任务。
如果场景不是封装一个 Promise
中的 nextTick
,可以进一步更为通用的微任务方法:
function microTask(func) {
// 1. 有 Promise 就用 Promise 创建的 microTask
if (typeof Promise !== "undefined") {
Promise.resolve().then(func);
} else if (typeof MutationObserver !== "undefined") {
// 2.没有 Promise, 就看是否存在 MutationObserver
const ob = new MutationObserver(func);
const textNode = document.createTextNode("0");
ob.server(textNode, {
characterData: true,
});
textNode.data = "1";
} else {
// 3.最后,使用 setTimeout 兜底。
setTimeout(func);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 6. 各种静态函数实现
上述已经实现了 PromiseCore
类基础功能,可以在此基础上封装便捷的工具函数。
Promise.resolve()
创建一个fulfilled
状态的Promise
Promise.reject()
创建一个rejected
状态的Promise
Promise.all
并发执行Promise
列表,缺点是不允许支持失败。Promise.allSettled
并发执行Promise
列表,弥补all
不允许失败的问题。Promise.race
竞速函数,捕获第一次返回结果无论fulfilled
或者rejected
状态。Promise.any
竞速函数 2.0 版本,只捕获成功返回,当所有都错误时才返回错误。Promise.finally
虽然用的比较少,但此函数的作用一举打破了promise
必须有明确状态的时才允许数据流通。一般可以将.then
和.catch
公共的部分提取出来写到此函数中。finally
本身不会对类状态做任何改变,即使返回状态也不会透传下去。
# 6.1 resolve[有些问题]
测试案例:
import { describe, expect, it, vi } from "vitest";
import myPromise from "./myPromise";
describe("测试 Promise", () => {
it("测试 Promise.resolve 接受普通类型 ", () =>
new Promise<void>((done) => {
myPromise.resolve("成功").then((res) => {
expect(res).toMatchInlineSnapshot('"成功"');
done();
});
}));
it("测试 Promise.resolve 接受 Promise ", () =>
new Promise<void>((done) => {
const promise = new myPromise((resolve) => resolve("成功"));
myPromise.resolve(promise).then((res) => {
expect(res).toMatchInlineSnapshot('"成功"');
done();
});
}));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
功能代码:
import { PromiseCore } from "./promiseCore";
PromiseCore.resolve = (parameter) => {
/* 注:resolve 支持接受 promise */
if (parameter instanceof PromiseCore) {
return parameter;
}
/* 否则构造一个 new Promise 返回
由 PromiseCore 处理 自引用|thenable|普通类型等返回值类型
*/
return new PromiseCore((resolve) => resolve(parameter));
};
2
3
4
5
6
7
8
9
10
11
# 6.2 reject
测试案例:
it("测试 Promise.reject 接受错误 ", () =>
new Promise<void>((done) => {
const promise = new myPromise((resolve, reject) => reject("失败原因"));
myPromise.resolve(promise).then(null, (reason) => {
expect(reason).toMatchInlineSnapshot('"失败原因"');
done();
});
}));
2
3
4
5
6
7
8
功能代码:
PromiseCore.reject = (parameter) => {
/* 构造一个新的 Promise 并返回 */
return new PromiseCore((resolve, reject) => reject(parameter));
};
2
3
4
# 6.3 all
测试案例:
it("测试 Promise.all 功能 -- 成功输入空数组", () =>
new Promise<void>((done) => {
myPromise.all([]).then((res) => {
expect(res).toMatchInlineSnapshot("[]");
done();
});
}));
it("测试 Promise.all 功能 -- 成功", () =>
new Promise<void>((done) => {
const p1 = 1;
const p2 = new myPromise((resolve) => resolve(2));
const p3 = myPromise.resolve(3);
const p4 = myPromise.reject("err4");
myPromise.all([p1, p2, p3]).then((res) => {
expect(res).toMatchInlineSnapshot(`
[
1,
2,
3,
]
`);
done();
});
}));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
功能代码:
PromiseCore.all = (promiseList: any[]) => {
/* 记录结果数组 */
const result = [] as any[];
let resCount = 0;
return new PromiseCore((resolve, reject) => {
if (promiseList.length === 0) {
resolve([]);
}
promiseList.forEach((p, i) => {
PromiseCore.resolve(p).then(
(res) => {
result[i] = res;
resCount++;
if (resCount === promiseList.length) {
resolve(result);
}
},
(reason) => {
reject(reason);
},
);
});
});
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.4 allSettled
测试代码:
it("测试 Promise.allSettled 功能", () =>
new Promise<void>((done) => {
const p1 = 1;
const p2 = new myPromise((resolve) => resolve(2));
const p3 = myPromise.resolve(3);
const p4 = myPromise.reject("err4");
myPromise.allSettled([p1, p2, p4]).then((res) => {
expect(res).toMatchInlineSnapshot(`
[
{
"status": "fulfilled",
"value": 1,
},
{
"status": "fulfilled",
"value": 2,
},
{
"reason": "err4",
"status": "rejected",
},
]
`);
done();
});
}));
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
功能代码:
PromiseCore.allSettled = (promiseList: any[]) => {
const result = [] as any[];
let resovleCount = 0;
return new PromiseCore((resolve) => {
if (promiseList.length === 0) {
resolve([]);
}
promiseList.forEach((p, i) => {
PromiseCore.resolve(p).then(
(res) => {
resovleCount++;
result[i] = {
status: "fulfilled",
value: res,
};
if (resovleCount === promiseList.length) {
return resolve(result);
}
},
(reason) => {
resovleCount++;
result[i] = {
status: "rejected",
reason: reason,
};
if (resovleCount === promiseList.length) {
return resolve(result);
}
},
);
});
});
};
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
# 6.5 race
测试代码:
it("测试 Promise.race 功能 - 第一个返回成功", () =>
new Promise<void>((done) => {
const p1 = new myPromise((reject) => {
setTimeout(() => reject(200), 200);
});
const p2 = new myPromise((resolve) => {
setTimeout(() => resolve(100), 100);
});
myPromise.race([p1, p2]).then((res) => {
expect(res).toMatchInlineSnapshot("100");
done();
});
}));
it("测试 Promise.race 功能 - 第一个返回失败", () =>
new Promise<void>((done) => {
const p1 = new myPromise((resolve) => {
setTimeout(() => resolve(200), 200);
});
const p2 = new myPromise((resolve, reject) => {
setTimeout(() => reject(100), 100);
});
myPromise.race([p1, p2]).catch((reason) => {
expect(reason).toMatchInlineSnapshot("100");
done();
});
}));
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
功能代码:
PromiseCore.race = (promiseList: any[]) => {
return new PromiseCore((resolve, reject) => {
if (promiseList.length === 0) {
resolve([]);
}
promiseList.forEach((p) => {
PromiseCore.resolve(p).then(
(res) => resolve(res),
(reason) => reject(reason),
);
});
});
};
2
3
4
5
6
7
8
9
10
11
12
13
# 6.6 any
测试代码:
it("测试 Promise.any 功能 - 失败fast,成功slow", () =>
new Promise<void>((done) => {
const p1 = new myPromise((resolve) => {
setTimeout(resolve, 50, "成功-slow");
});
const p2 = new myPromise((resolve, reject) => {
setTimeout(reject, 10, "失败-quick");
});
myPromise.any([p1, p2]).then((res) => {
expect(res).toMatchInlineSnapshot('"成功-slow"');
done();
});
}));
it("测试 Promise.any 功能 - 均失败", () =>
new Promise<void>((done) => {
const p1 = myPromise.reject("err1");
const p2 = myPromise.reject("err2");
myPromise.any([p1, p2]).catch((reason) => {
expect(reason).toMatchInlineSnapshot('"All Promise task failed"');
done();
});
}));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
功能代码:
PromiseCore.any = (promiseList: any[]) => {
let rejectCount = 0;
return new PromiseCore((resolve, reject) => {
if (promiseList.length === 0) {
resolve([]);
}
promiseList.forEach((p) => {
PromiseCore.resolve(p).then(
(res) => resolve(res),
(reason) => {
rejectCount++;
if (rejectCount === promiseList.length) {
reject("All Promise task failed");
}
},
);
});
});
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 6.7 finally
测试代码:
it("测试 Promise.finally 功能 - 继承 成功 状态", () =>
new Promise<void>((done) => {
new myPromise((resolve, reject) => {
resolve("success");
})
.finally(
() =>
new myPromise((resolve) => {
setTimeout(() => {
resolve(111);
}, 10);
}),
)
.then((data) => {
expect(data).toMatchInlineSnapshot('"success"');
done();
});
}));
it("测试 Promise.finally 功能 - 继承 失败 状态", () =>
new Promise<void>((done) => {
new myPromise((resolve, reject) => {
reject("error1");
})
.finally(
() =>
new myPromise((resolve) => {
setTimeout(() => {
resolve(111);
}, 10);
}),
)
.then(null, (reason) => {
expect(reason).toMatchInlineSnapshot('"error1"');
done();
});
}));
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
功能代码:
/* 绑定在原型上,所有实例都将继承 finally 属性 */
PromiseCore.prototype.finally = function(cb) {
/* Promise.finally 将透传 Promise 的状态 !!! */
return new Promise((resolve, reject) => {
/* 继承 this 的结果 */
this.then(
(value) => PromiseCore.resolve(cb()).then(() => resolve(value)),
(reason) => PromiseCore.resolve(cb()).then(() => reject(reason)),
);
});
};
2
3
4
5
6
7
8
9
10
11