第 15 章 Promise
第 15 章 Promise
Promise 是 JavaScript 异步编程的里程碑——它解决了回调地狱问题,让异步代码看起来像同步代码。学了这一章,你就能优雅地处理"等会儿再告诉你"的场景了!
15.1 Promise 基础
Promise 的三种状态:pending / fulfilled / rejected
Promise 是一个对象,它代表一个异步操作的最终结果。就像一个承诺——“我保证以后会告诉你结果”。
Promise 有三种状态:
| 状态 | 含义 | 能否转变 |
|---|
pending | 进行中(初始状态) | → fulfilled 或 rejected |
fulfilled | 已成功 | → 不能再改变 |
rejected | 已失败 | → 不能再改变 |
pending
/ \
↓ ↓
fulfilled rejected
状态一旦改变,就不能再改变了。这叫做 Promise 的不可逆性。
状态不可逆
1
2
3
4
5
6
| const promise = new Promise((resolve, reject) => {
resolve("第一次成功");
reject("第二次失败"); // 这行无效,因为已经 resolved
});
promise.then(console.log); // "第一次成功"
|
创建 Promise:new Promise((resolve, reject) => {})
创建 Promise 使用 new Promise 构造函数,传入一个执行器函数(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
| const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve("操作成功!"); // 标记为 fulfilled
} else {
reject("操作失败了!"); // 标记为 rejected
}
}, 1000);
});
console.log("Promise 已创建(pending 状态)");
promise
.then(result => {
console.log("成功:" + result);
})
.catch(error => {
console.log("失败:" + error);
});
console.log("这段代码在 Promise 创建后立即执行");
|
执行顺序:
- “Promise 已创建(pending 状态)”
- “这段代码在 Promise 创建后立即执行”
- (1秒后)“成功:操作成功!”
💡 因为 Promise 内部是异步执行的,Promise 构造器中的代码会立即执行,但 resolve/reject 会放到微任务队列中。
15.2 Promise 方法
then:处理成功
then 方法用于处理 Promise 成功(fulfilled)的状态:
1
2
3
4
5
| const promise = Promise.resolve("成功了!");
promise.then(result => {
console.log(result); // "成功了!"
});
|
then 接收一个回调函数,当 Promise 变为 fulfilled 状态时调用。
catch:处理失败
catch 方法用于处理 Promise 失败(rejected)的状态:
1
2
3
4
5
| const promise = Promise.reject("出错了!");
promise.catch(error => {
console.log(error); // "出错了!"
});
|
也可以用 then 的第二个参数处理失败:
1
2
3
4
5
6
| Promise.reject("出错了!")
.then(
result => console.log(result),
error => console.log("失败:" + error)
);
// "失败:出错了!"
|
finally:无论如何都执行
finally 方法的回调无论 Promise 是成功还是失败都会执行,适合做清理工作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| function fetchData() {
return new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
setTimeout(() => {
if (success) {
resolve("数据");
} else {
reject("网络错误");
}
}, 1000);
});
}
fetchData()
.then(data => console.log("成功:" + data))
.catch(error => console.log("失败:" + error))
.finally(() => {
console.log("无论成功失败,都会执行这个清理操作");
// 比如:关闭 loading 动画
});
|
链式调用
Promise 的 then、catch、finally 都返回一个新的 Promise,支持链式调用:
1
2
3
4
5
6
7
8
| Promise.resolve(1)
.then(x => x + 1) // 2
.then(x => x + 2) // 4
.then(x => {
console.log(x); // 4
return x + 3;
})
.then(x => console.log(x)); // 7
|
返回值传递
每个 then 的返回值会传递给下一个 then:
1
2
3
4
5
| Promise.resolve(5)
.then(x => x * 2) // 10
.then(x => x + 1) // 11
.then(x => ({ value: x })) // 返回对象
.then(obj => console.log(obj.value)); // 11
|
忘记 return / return undefined 的坑
这是一个常见的坑!如果 then 的回调函数没有 return,会隐式返回 undefined,后续的 then 就会收到 undefined:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // ❌ 错误示例
Promise.resolve(5)
.then(x => {
x * 2; // 没有 return!
})
.then(x => {
console.log(x); // undefined!
});
// ✅ 正确示例
Promise.resolve(5)
.then(x => {
return x * 2; // 有 return
})
.then(x => {
console.log(x); // 10
});
|
catch 之后 return 的值进入下一个 then
如果你在 catch 中 return 一个值,它会进入下一个 then:
1
2
3
4
5
6
7
8
| Promise.reject("出错了")
.catch(error => {
console.log("捕获:" + error); // "捕获:出错了"
return "恢复的值";
})
.then(value => {
console.log("恢复后:" + value); // "恢复后:恢复的值"
});
|
如果在 catch 中抛出错误,会进入下一个 catch:
1
2
3
4
5
6
7
| Promise.reject("出错了")
.catch(error => {
throw new Error("重新抛出");
})
.catch(error => {
console.log("新的错误:" + error.message); // "新的错误:重新抛出"
});
|
15.3 Promise 类方法
Promise.resolve() / Promise.reject()
这两个静态方法用于创建已经 resolved 或 rejected 的 Promise:
1
2
3
4
5
6
7
| // 已经 resolved 的 Promise
const resolvedPromise = Promise.resolve("成功了");
resolvedPromise.then(console.log); // "成功了"
// 已经 rejected 的 Promise
const rejectedPromise = Promise.reject("失败了");
rejectedPromise.catch(console.log); // "失败了"
|
Promise.resolve() 还会展开 thenable 对象(具有 .then 方法的对象):
1
2
3
4
5
6
7
| const thenable = {
then(resolve, reject) {
resolve("从 thenable 来");
}
};
Promise.resolve(thenable).then(console.log); // "从 thenable 来"
|
Promise.all():全部成功才成功,用于并发请求
Promise.all 接收一个 Promise 数组,只有当所有 Promise 都成功时,整个 Promise 才成功;如果任何一个失败,整个 Promise 就失败。
1
2
3
4
5
6
7
8
| const promise1 = Promise.resolve("结果1");
const promise2 = Promise.resolve("结果2");
const promise3 = Promise.resolve("结果3");
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // ["结果1", "结果2", "结果3"]
});
|
典型应用:并发请求并等待所有结果
1
2
3
4
5
6
7
8
9
10
11
12
| function fetchUser(userId) {
return fetch(`/api/user/${userId}`).then(res => res.json());
}
Promise.all([
fetchUser(1),
fetchUser(2),
fetchUser(3)
]).then(([user1, user2, user3]) => {
console.log("所有用户加载完成");
console.log(user1, user2, user3);
});
|
Promise.all() 的错误处理:任一失败则整体失败
1
2
3
4
5
6
7
8
9
10
| const promises = [
Promise.resolve("成功1"),
Promise.reject("失败2"),
Promise.resolve("成功3")
];
Promise.all(promises)
.then(results => console.log(results))
.catch(error => console.log("整体失败:" + error));
// "整体失败:失败2"
|
Promise.allSettled()(ES2020+):等待所有 Promise 结束
Promise.allSettled 无论成功还是失败都会等待所有 Promise 完成,返回每个 Promise 的详细状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| const promises = [
Promise.resolve("成功1"),
Promise.reject("失败2"),
Promise.resolve("成功3")
];
Promise.allSettled(promises).then(results => {
console.log(results);
// [
// { status: "fulfilled", value: "成功1" },
// { status: "rejected", reason: "失败2" },
// { status: "fulfilled", value: "成功3" }
// ]
});
|
💡 什么时候用?当你需要知道所有请求的结果(无论成功失败)时,用 allSettled;当你需要任一失败就算整体失败时,用 all。
Promise.race():返回最快的,用于超时处理
Promise.race 返回最快完成的那个 Promise(无论成功还是失败):
1
2
3
4
5
6
| const slow = new Promise(resolve => setTimeout(() => resolve("慢"), 1000));
const fast = new Promise(resolve => setTimeout(() => resolve("快"), 500));
Promise.race([slow, fast])
.then(result => console.log("获胜的是:" + result));
// "获胜的是:快"(1秒后)
|
典型应用:请求超时
1
2
3
4
5
6
7
8
9
10
11
12
| function fetchWithTimeout(url, timeout = 3000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error("请求超时")), timeout)
);
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchWithTimeout("/api/data", 1000)
.then(data => console.log(data))
.catch(error => console.log(error.message)); // "请求超时"(如果1秒内没响应)
|
Promise.any()(ES2021+):返回第一个成功的,忽略失败
Promise.any 与 race 类似,但返回第一个成功的 Promise,忽略失败:
1
2
3
4
5
6
7
8
9
| const promises = [
Promise.reject("失败1"),
Promise.resolve("成功2"),
Promise.resolve("成功3")
];
Promise.any(promises)
.then(result => console.log("第一个成功:" + result));
// "第一个成功:成功2"
|
如果所有 Promise 都失败了,会抛出 AggregateError:
1
2
3
4
5
6
7
8
| Promise.any([
Promise.reject("失败1"),
Promise.reject("失败2")
])
.then(result => console.log(result))
.catch(error => {
console.log(error.errors); // ["失败1", "失败2"]
});
|
all vs allSettled vs race vs any 选择指南
| 方法 | 成功条件 | 失败条件 | 返回值 |
|---|
all | 全部成功 | 任一失败 | 所有结果的数组 |
allSettled | 总是等待所有完成 | 从不失败 | 每个 Promise 的状态对象 |
race | 任意一个完成 | 任意一个失败 | 最快完成的结果 |
any | 任意一个成功 | 全部失败 | 第一个成功的结果 |
15.4 Promise 进阶
Promise 的微任务本质:then 是微任务
Promise 的 .then、.catch、.finally 的回调都是作为微任务执行的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| console.log("1(同步)");
Promise.resolve()
.then(() => console.log("3(微任务)"));
setTimeout(() => console.log("4(宏任务)"), 0);
console.log("2(同步)");
// 输出:
// 1(同步)
// 2(同步)
// 3(微任务)
// 4(宏任务)
|
new Promise 构造函数是同步执行的
Promise 构造器中的代码是同步执行的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| console.log("1");
new Promise((resolve, reject) => {
console.log("2(Promise 内部同步执行)");
resolve();
}).then(() => console.log("4(微任务)"));
console.log("3");
// 输出:
// 1
// 2(Promise 内部同步执行)
// 3
// 4(微任务)
|
thenable 对象
thenable 是具有 .then 方法的对象,Promise.resolve() 会"展开"它:
1
2
3
4
5
6
7
8
| const thenable = {
then(resolve, reject) {
setTimeout(() => resolve("resolved!"), 1000);
}
};
Promise.resolve(thenable)
.then(value => console.log(value)); // "resolved!"(1秒后)
|
这意味着任何具有 .then 方法的对象都可以当作 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
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
| class SimplePromise {
constructor(executor) {
this.state = "pending";
this.value = undefined;
this.callbacks = [];
const resolve = (value) => {
if (this.state !== "pending") return;
this.state = "fulfilled";
this.value = value;
this.callbacks.forEach(cb => cb.onFulfilled(value));
};
const reject = (reason) => {
if (this.state !== "pending") return;
this.state = "rejected";
this.value = reason;
this.callbacks.forEach(cb => cb.onRejected(reason));
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
return new SimplePromise((resolve, reject) => {
const handleCallback = (callback, fallback) => {
try {
const result = callback ? callback(this.value) : fallback(this.value);
resolve(result);
} catch (error) {
reject(error);
}
};
if (this.state === "fulfilled") {
queueMicrotask(() => handleCallback(onFulfilled, v => v));
} else if (this.state === "rejected") {
queueMicrotask(() => handleCallback(onRejected, throw it => { throw it; }));
} else {
this.callbacks.push({
onFulfilled: () => handleCallback(onFulfilled, v => v),
onRejected: () => handleCallback(onRejected, throw it => { throw it; })
});
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => { onFinally(); return value; },
reason => { onFinally(); throw reason; }
);
}
}
// 测试
new SimplePromise(resolve => resolve("成功"))
.then(value => console.log(value)); // "成功"
|
💡 这个简易 Promise 只实现了核心功能,真正完整的 Promise 实现(如 Promise/A+ 规范)要复杂得多。如果你想深入了解,可以去看 Promise/A+ 规范的实现。
手写 Promise.all
理解了 Promise.all 的原理,我们可以自己实现:
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
| function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;
if (promises.length === 0) {
resolve([]);
return;
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
results[index] = value;
completed++;
if (completed === promises.length) {
resolve(results);
}
})
.catch(reason => {
reject(reason);
});
});
});
}
// 测试
promiseAll([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(console.log); // [1, 2, 3]
promiseAll([
Promise.resolve(1),
Promise.reject("出错了"),
Promise.resolve(3)
]).catch(console.log); // "出错了"
|
本章小结
本章我们深入学习了 Promise:
Promise 基础:
- 三种状态:pending / fulfilled / rejected
- 状态不可逆
new Promise 创建
Promise 方法:
then:处理成功catch:处理失败finally:无论如何都执行- 链式调用和返回值传递
- 忘记 return 的坑
Promise 类方法:
Promise.resolve/rejectPromise.all:全部成功才成功Promise.allSettled:等待所有完成Promise.race:返回最快的Promise.any:返回第一个成功的
Promise 进阶:
- then 是微任务
- thenable 对象
- 手写简易 Promise
- 手写 Promise.all
📊 图示:Promise 状态转换
stateDiagram-v2
[*] --> Pending: new Promise
Pending --> Fulfilled: resolve()
Pending --> Rejected: reject()
Fulfilled --> [*]
Rejected --> [*]
note right of Fulfilled
状态不可逆
end note
下章预告:下一章我们将学习 async/await 与 Generator——让异步代码看起来像同步代码! 🚀