Promise 延展

Promise

什么是promise

作为异步函数,有四种状态

Pending 进行中(Pending) 用 new Promise() 创建对象

Fulfilled 成功(Resolved) 用 .then() 处理成功的结果

Rejected 失败 (Rejected) 用 .catch() 处理失败

Finally 结束 () 用 .finally() 结束

怎么用

异步函数

1
2
3
4
5
6
7
8
9
10
11
const step_1 = new Promise((resolve, reject) => { //Promise对象
setTimeout(() => resolve("奶茶"), 1000);
});

const step_2 = (tea) => new Promise((resolve, reject) => { //函数
setTimeout(() => resolve(tea + "+珍珠"), 1000);
});

const step_3 = (tea) => new Promise((resolve, reject) => { //函数
setTimeout(() => resolve(tea + "+奶盖"), 1000);
});

链式调用

1
2
3
4
5
6
7
8
9
10
11
12
step_1
.then(tea => step_2(tea))
.then(tea => step_3(tea))
.then(tea => {
console.log("最终奶茶:", tea);
})
.catch(error => {
console.log("失败:", error);
})
.finally(() => {
console.log("结束并清理");
});
1
2
3
// 3s后打印
最终奶茶:奶茶+珍珠+奶盖
结束并清理

普通函数的回调地狱

每次回调都要判断error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
step_1((error, tea) => {
if (error) {
console.log("失败:", error);
} else {
step_1(tea, (error, tea) => {
if (error) {
console.log("失败:", error);
} else {
step_1(tea, (error, tea) => {
if (error) {
console.log("失败:", error);
} else {
console.log("最终奶茶:", tea);
}
});
}
});
}
});
使用场景

网络请求

fetch返回一个promise对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function fetchData(url) {
const headers = {
"Content-Type": "application/json",
...options.headers,
};
return fetch(url)
.then(response => response.json())
.catch(error => {
throw error; // 或直接返回错误信息
});
}

fetchData("https://api.example.com/data1")
.then(data => console.log("数据加载成功:", data))
.catch(error => console.error("数据加载失败:", error));
fetchData("https://api.example.com/data2")
.then(data => console.log("数据加载成功:", data))
.catch(error => console.error("数据加载失败:", error));

统一错误处理

回调函数中需要手动检查每个步骤的错误。

1
2
3
4
5
6
7
8
9
10
11
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("成功");
} else {
reject("失败");
}
}, 1000);
})
.then(result => console.log(result))
.catch(error => console.error("捕获错误:", error));

状态管理

异步操作有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)

1
2
3
4
5
6
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("完成"), 1000);
});

console.log(promise); // 输出: Promise { <pending> }
setTimeout(() => console.log(promise), 1500); // 输出: Promise { <fulfilled>: "完成" }

链式调用

1
2
3
4
5
step1()
.then(result1 => step2(result1))
.then(result2 => step3(result2))
.then(finalResult => console.log(finalResult))
.catch(error => console.error(error));

手写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
class MyPromise {
constructor(executor) {
this.state = 'pending'; // Promise的状态:'pending', 'fulfilled', 'rejected'
this.value = undefined; // 成功时的值
this.reason = undefined; // 失败时的原因
this.onFulfilledCallbacks = []; // 存储成功回调
this.onRejectedCallbacks = []; // 存储失败回调

const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};

const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};

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

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

let promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}

if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}

if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});

this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});

return promise2;
}

catch(onRejected) {
return this.then(null, onRejected);
}
}

// 辅助函数,处理promise链中的值传递
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}

let called;

if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if (called) return;
called = true;
reject(err);
});
} else {
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(x);
}
}

什么是promise.all()

等待所有兑现(或第一个拒绝)的结果

怎么用

例子1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const step_1 = new Promise((resolve, reject) => { //Promise对象
setTimeout(() => resolve("奶茶"), 1000);
});

const step_2 = (tea) => new Promise((resolve, reject) => { //函数
setTimeout(() => resolve(tea + "+珍珠"), 1000);
});

const step_3 = (tea) => new Promise((resolve, reject) => { //函数
setTimeout(() => resolve(tea + "+奶盖"), 1000);
});

const step_4 = new Promise((resolve, reject) => { //Promise对象
setTimeout(() => resolve("最终收拾"), 1000);
});
1
2
3
Promise.all([step_1, step_4]).then((allstep) => {
console.log(allstep);
});
1
["奶茶", "最终收拾"] //1秒后打印

例子2

1
2
3
4
5
6
7
8
9
10
11
12
step_1
.then(tea => {
return Promise.all([
Promise.resolve(tea),// step_1 的结果
step_2(tea), // step_2 的结果
step_3(tea), // step_3 的结果
step_4 // step_4 的结果
]);
})
.then(results => {
console.log(results); // 输出所有结果
});
1
["奶茶", "奶茶+珍珠", "奶茶+奶盖", "最终收拾"] //2秒后打印

例子3

1
2
3
4
5
6
7
8
9
10
11
const promises = [];

for (let i = 0; i < 10; i++) {
promises.push(new Promise((resolve) => {
setTimeout(() => resolve(`Promise ${i} 完成`), 1000);
}));
}

Promise.all(promises).then(results => {
console.log(results);
});
1
2
3
4
5
6
7
8
9
10
11
12
[
"Promise 0 完成",
"Promise 1 完成",
"Promise 2 完成",
"Promise 3 完成",
"Promise 4 完成",
"Promise 5 完成",
"Promise 6 完成",
"Promise 7 完成",
"Promise 8 完成",
"Promise 9 完成"
] //1秒后打印
使用场景

常用于数据聚合、资源加载、并发任务等场景

并行网络请求

1
2
3
4
5
6
7
8
9
const request1 = fetch("https://api.example.com/data1").then(res => res.json());
const request2 = fetch("https://api.example.com/data2").then(res => res.json());

Promise.all([request1, request2])
.then(([data1, data2]) => {
console.log("请求1结果:", data1);
console.log("请求2结果:", data2);
})
.catch(error => console.error("请求失败:", error));

数据聚合

Promise.all 自动收集所有结果为一个数组,方便后续处理。

1
2
3
4
5
6
7
8
9
const getUser = () => fetch("/user").then(res => res.json());
const getOrders = () => fetch("/orders").then(res => res.json());

Promise.all([getUser(), getOrders()])
.then(([user, orders]) => {
console.log("用户信息:", user);
console.log("订单列表:", orders);
})
.catch(error => console.error("数据加载失败:", error));

快速失败

1
2
3
4
5
6
7
const task1 = new Promise(resolve => setTimeout(() => resolve(1), 1000));
const task2 = new Promise((_, reject) => setTimeout(() => reject("出错了"), 500));
const task3 = new Promise(resolve => setTimeout(() => resolve(3), 1000));

Promise.all([task1, task2, task3])
.then(results => console.log("所有任务完成:", results))
.catch(error => console.error("任务失败:", error)); // 输出: 任务失败: 出错了

统一处理多个异步任务

避免多次调用 .then() 或手动合并结果。

1
2
3
4
5
6
7
8
9
10
const tasks = [
new Promise(resolve => setTimeout(() => resolve("任务1完成"), 1000)),
new Promise(resolve => setTimeout(() => resolve("任务2完成"), 1000)),
new Promise(resolve => setTimeout(() => resolve("任务3完成"), 1000))
];

Promise.all(tasks)
.then(results => console.log("所有任务结果:", results))
.catch(error => console.error("任务失败:", error));
// 输出: 所有任务结果: ["任务1完成", "任务2完成", "任务3完成"]

手写promise.all()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function myPromiseAll(promises) {
if (Array.isArray(promises)) { // 是数组
return new Promise((resolve, reject) => { // 每个元素都转换为 Promise
let count = 0 // 记录已完成的任务数量
const results = []

promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
results[index] = value // 根据索引存
count ++
if (count === promises.length) {
resolve(results)
}
})
.catch(reject)
})
})
} else {
return Promise.reject(new TypeError('传入的参数不是数组'))
}
}
1
2
3
4
5
6
7
myPromiseAll([
new Promise((resolve) => setTimeout(() => resolve(1), 3000)),
new Promise((resolve) => setTimeout(() => resolve(2), 2000)),
new Promise((resolve) => setTimeout(() => resolve(3), 1000)),
])
.then(console.log)
.catch(console.error)
1
[1,2,3]