Promise 总结

回调地狱

1
2
3
4
5
6
7
8
9
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 1000);
}, 1000);
}, 1000);

缺点:

  • 代码耦合性太强, 牵一发而动全身, 难以维护

  • 大量冗余代码相互嵌套, 代码可读性变差

如何解决回调地狱的问题

为了解决回调地狱的问题, ES6 新增了 Promise ​ 的概念

Promise 的三种状态

Promise 通过自身状态, 来控制异步操作, Promise 实例有三种状态

  • 异步操作未完成(pending)

  • 异步操作成功(fulfilled)

  • 异步操作失败(rejected)

这三种的状态变化途径只有两种

  • 从未完成到成功

  • 从未完成到失败

一旦状态发生变化, 就会凝固, 不会发生新变化, Promise 的状态变化只会发生一次

  • 异步操作成功,Promise 实例传回一个值(va1ue),状态变为 fulfilled

  • 异步操作失败,Promise 实例抛出一个错误(error),状态变为 rejected

Snipaste_2023-05-02_16-01-52

Promise 的链式调用

promise 支持链式调用, 通过 return 一个 promise实例 来链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
const res = Math.random() > 0.5;
if (res) resolve(res);
else reject(res);
}, 1000);
});

pro.then(res => {
console.log(res);
// 如果返回的是非Promise对象, 那么默认转换为fulfilled,并将return的结果传递到下一个then
return 123;
})
.then(res => {
console.log(res); // 123
})
.catch(res => {
console.log(res);
});

注意:

  • 如果返回的是非 Promise ​ 对象, 那么默认转换为 fulfilled,并将 return ​ 的结果传递到下一个 then

  • 如果返回 Promise 对象, 那么返回的对象的状态取决于 Promise 实例的状态

通过.catch 捕获错误

在 Promise 的链式操作中, 可以通过 .catch 来捕获错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tfs.readFile("./txt/1.txt", "utf8")
.then(result => {
console.log(result);
return tfs.readFile("./txt/12.txt", "utf8");
})
.then(result => {
console.log(result);
return tfs.readFile("./txt/3.txt", "utf8");
})
.then(result => {
console.log(result);
})
.catch(err => {
console.log(err);
});

Promise 的对象方法

Promise是个对象, 也是一个构造函数

Promise.resolve()

返回一个 resolved 状态的 promise 实例

Promise.reject()

返回一个 reject 状态的 promise 实例

以上两个方法如果传入 promise 对象, 那么会原样返回, 不会更改实例状态

Promise.all()方法

Promise.all() 方法可以并行执行多个任务, 并且等到所有任务都 fulfilled 之后再执行 .then() 方法, 如果有一个 reject, 那么状态就会变成 reject

注意:

  • 只有 p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数。

  • 只要 pl、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。

Promise.race() 方法

Promise.race() 方法会发起并行的 Promise 异步操作, 只要有一个任务 fulfilledreject, 整个 promise 就会 fulfilledreject

Promise.allSettled()方法

该方法在全部的 promise ​ 结束之后(成功或失败)执行 .then, 返回一个包含状态和结果值得对象

1
2
3
4
5
6
7
8
Promise.allSettled([pro(1), pro(-1), pro(1)]).then(res => {
console.log(res);
});
[
{ status: "fulfilled", value: "123" },
{ status: "rejected", reason: "err" },
{ status: "fulfilled", value: "123" },
];

Promise.any()

只要有一个任务变成 fulfilled, 那么整个就会 fulfilled, 只有全部 reject, 那么才会 reject,

race 的区别, race 只要第一个 reject, 那么整体就会 reject, 而 any 则是全部 reject 才会 reject

Promise 系列方法总结

方法名称 resolved 的条件 reject 的条件 resolve 返回值
all 所有 Promise 实例都成功时 任意一个 Promise 实例失败时 包含所有实例结果数组
race 第一个 Promise 实例状态变为 resolved 时 第一个 Promise 实例状态变为 rejected 时 第一个实例返回的结果
allSettled 所有 Promise 实例完成,不论是否成功或失败 不会抛出异常 包含每个实例状态及结果的数组
any 第一个 Promise 实例成功时 所有 Promise 实例均失败时 第一个实例的返回值

封装 Promise 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
const res = Math.random() > 0.5;
if (res) resolve(res);
else reject(res);
}, 1000);
});

pro.then(res => {
console.log(res);
// 如果返回的是非Promise对象, 那么默认转换为fulfilled,并将return的结果传递到下一个then
return 123;
})
.then(res => {
console.log(res); // 123
})
.catch(res => {
console.log(res);
});

async/await

async/await 是 es8 (ECMA 2017)引入的新语法, 用来简化 Promise 的异步操作, 在 async/await 出现之前, 开发只能通过链式 .then 的方法处理 Promise 异步操作

基本使用

如果在一个 Promise 实例的前面加上 await, 那么可以直接获取到该实例的返回值

1
2
3
4
5
6
7
8
9
async function getallFile() {
const r1 = await getFile("./txt/1.txt");
console.log(r1);
const r2 = await getFile("./txt/2.txt");
console.log(r2);
const r3 = await getFile("./txt/3.txt");
console.log(r3);
}
getallFile();

async/await 的注意事项

  • 如果 function 内部使用了 await 修饰, 那么 function 必须使用 async 修饰

  • async 方法中, 第一个 await 之前的代码会同步执行, await 之后的代码会异步执行

  • 如果在 async 函数内返回一个普通值, async 函数会自动将该值包装成一个 Promise 对象,并直接 resolved

  • 如果 async 函数内返回一个 promise 对象, async ​ 函数返回的仍然是一个 Promise 对象,但其实际结果取决于所返回的 Promise 的状态

顶层 await

可以在 es 模块顶层中使用 await

1
<script type="module">await xxx</script>

手写 Promise

Promise/A+标准

Promise/A+ 是 JavaScript 中 Promise 对象的一个规范,它是 Promises/A+ Working Group 提出的标准。这个组织成立于 2010 年,旨在推动 Promise 的发展并制定一致的行业规范。

Promise/A+ 主要定义了 Promise 对象的行为和特征,以确保不同实现之间具有良好的互操作性。其中包括:

  1. Promise 是一个对象或函数,具有 then 方法。

  2. then 方法接受两个参数:onFulfilled 和 onRejected,分别代表 Promise 成功时和失败时的回调函数。

  3. then 方法返回一个新的 Promise 对象,并且可以链式调用多次,每次调用都会返回一个新的 Promise 对象。

  4. 最终状态只能是成功(fulfilled)或者失败(rejected),并且状态不可逆转。

  5. 如果某个 then 方法中的回调函数返回了一个值 x,则根据 x 的类型来处理该值:

    • 如果 x 是一个 Promise 对象,则等待该 Promise 对象的状态变更,然后根据其最终状态处理当前 Promise 对象;
    • 如果 x 是一个普通对象或函数,则将当前 Promise 对象的状态设为 fulfilled,并把 x 作为新的 Promise 对象传递给下一个 then 方法;
    • 如果 x 失败,则将当前 Promise 对象的状态设为 rejected,并把 x 的错误信息向下传递给下一个 catch 方法。

通过符合 Promise/A+ 规范,就能确保不同的 Promise 实现都遵循相同的方法调用和状态转移逻辑,从而简化了 JavaScript 中异步编程的复杂性。

手写 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
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
enum PromiseStatus {
"pending",
"fulfilled",
"rejected",
}
export class MyPromise {
// promise内部状态
status = PromiseStatus.pending;
// promise结果
result: any = undefined;
// 回调函数列表
callback: {
successCB: Function;
failCB: Function;
}[] = [];
constructor(executor: (resolve: Function, reject: Function) => void) {
// promise被new的同时, 执行器函数就运行
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(value: any) {
// promise的状态一经改变, 就会凝固
if (this.status != PromiseStatus.pending) {
return;
}
this.status = PromiseStatus.fulfilled;
this.result = value;
// 执行异步回调
this.callback.forEach(item => {
item.successCB();
});
}
reject(value: any) {
if (this.status != PromiseStatus.pending) {
return;
}
this.status = PromiseStatus.rejected;
this.result = value;
this.callback.forEach(item => {
item.failCB();
});
}
then(
successCB: ((resolveResult: any) => MyPromise | any) | undefined,
failCB?: (rejectResult: any) => MyPromise | any,
) {
// 如果只给了成功回调, 那么失败回调就返回参数
if (!failCB) {
failCB = err => err;
}
return new MyPromise((resolve, reject) => {
// 判断promise状态
// 同步执行
if (this.status == PromiseStatus.fulfilled) {
// 获取回调的返回值, 作为promise的返回值
const cbResult = successCB && successCB(this.result);
this.handleCBResult(cbResult, resolve, reject);
}
if (this.status == PromiseStatus.rejected) {
const cbResult = failCB && failCB(this.result);
this.handleCBResult(cbResult, resolve, reject);
}
// 异步执行
if (this.status == PromiseStatus.pending) {
// 将回调入栈, 可能执行多次then, 所以使用数组存储回调
this.callback.push({
successCB: () => {
const cbResult = successCB && successCB(this.result);
this.handleCBResult(cbResult, resolve, reject);
},
failCB: () => {
const cbResult = failCB && failCB(this.result);
this.handleCBResult(cbResult, resolve, reject);
},
});
}
});
}
catch(failCB: (rejectResult: any) => MyPromise | any) {
return this.then(undefined, failCB);
}
// 检查是否是promise,如果不是, 直接resolve, 如果是, 等待结果并resolve或reject
handleCBResult(
cbResult: MyPromise | any,
resolve: Function,
reject: Function,
) {
if (cbResult instanceof MyPromise) {
cbResult.then(
res => {
resolve(res);
},
err => {
reject(err);
},
);
} else {
// 如果前一个promise的状态是reject, 那么就reject
if (this.status == PromiseStatus.rejected) {
reject(cbResult);
} else {
resolve(cbResult);
}
}
}
static all(taskArr: MyPromise[]) {
// 全部任务成功才resolve, 有一个失败就reject
return new MyPromise((resolve, reject) => {
const resultArr: any[] = [];
taskArr.forEach(task => {
task.then(
res => {
// 每一个任务成功之后都把结果放入结果列表
resultArr.push(res);
// 如果全部任务成功那么就resolve
if (resultArr.length === taskArr.length) {
resolve(resultArr);
}
},
err => {
reject(err);
},
);
});
});
}
static race(taskArr: MyPromise[]) {
//返回第一个完成的结果
return new MyPromise((resolve, reject) => {
for (const task of taskArr) {
task.then(
res => {
resolve(res);
},
err => {
reject(err);
},
);
}
});
}
static allSettled(taskArr: MyPromise[]) {
// 只有resolve, 所有任务完成后返回包含结果的数组
return new MyPromise((resolve, reject) => {
const resultArr: any[] = [];
taskArr.forEach(task => {
task.then(
res => {
resultArr.push({
status: "fulfilled",
value: res,
});
if (taskArr.length == resultArr.length) {
resolve(resultArr);
}
},
err => {
resultArr.push({
status: "rejected",
value: err,
});
if (taskArr.length == resultArr.length) {
resolve(resultArr);
}
},
);
});
});
}
static any(taskArr: MyPromise[]) {
// 返回第一个resolve的对象, 如果全部reject , 那么就reject
return new MyPromise((resolve, reject) => {
let errCount = 0;
taskArr.forEach(task => {
task.then(
res => {
resolve(res);
},
err => {
errCount++;
if (taskArr.length == errCount) {
reject(err);
}
},
);
});
});
}
}
作者

cuicui

发布于

2023-05-03

更新于

2023-05-03

许可协议

评论