1. 什么是 Promise? #
在 JavaScript / TypeScript 中,Promise 表示一个异步操作的最终结果(成功或失败)。
它有三种状态:
pending(进行中)
fulfilled(成功)
rejected(失败)
一旦状态从 pending
变成 fulfilled
或 rejected
,就不可再改变。
2. Promise 的语法 #
const p = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("ok"); // 成功
// reject(new Error("fail")); // 失败
}, 1000);
});
p.then(result => console.log("成功:", result))
.catch(err => console.error("失败:", err))
.finally(() => console.log("完成"));
解释:
resolve(value)
→ 将 Promise 状态设为 fulfilledreject(error)
→ 将 Promise 状态设为 rejected.then()
→ 成功时回调.catch()
→ 失败时回调.finally()
→ 无论成功失败都会执行
3. Promise 的链式调用 #
function task1(): Promise<number> {
return Promise.resolve(1);
}
function task2(n: number): Promise<number> {
return Promise.resolve(n + 1);
}
task1()
.then(res => task2(res))
.then(final => console.log("结果:", final)); // 输出 2
这里通过链式调用保证 前一个结果传给下一个异步任务。
4. Promise 并发执行 #
如果多个异步任务可以并行,推荐用 Promise.all:
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
Promise.all([p1, p2]).then(([a, b]) => {
console.log(a + b); // 3
});
还有:
Promise.race([p1, p2])
→ 谁先完成就返回谁Promise.allSettled([p1, p2])
→ 等待所有 Promise 都有结果(不管成功失败)
二、async/await vs Promise.then 对比 #
特性 | Promise.then 写法 | async/await 写法 |
---|---|---|
可读性 | 链式回调,容易嵌套多层 | 看起来像同步代码,线性结构 |
错误处理 | 需要 .catch() 或链式处理 | 用 try...catch ,和同步异常一样 |
调试体验 | 栈信息可能被截断 | 更接近同步栈,调试更清晰 |
并发执行 | Promise.all([...]) | 同样用 Promise.all([...]) ,但结合 await 更直观 |
返回值 | 直接得到 Promise | async 函数自动返回 Promise |
语法复杂度 | 初学者不直观 | 更贴近同步思维 |
示例对比 #
Promise.then 写法 #
function fetchData(): Promise<string> {
return new Promise(resolve => setTimeout(() => resolve("data"), 1000));
}
fetchData()
.then(res => {
console.log("Got:", res);
return res + " processed";
})
.then(final => console.log("Final:", final))
.catch(err => console.error("Error:", err));
async/await 写法 #
async function main() {
try {
const res = await fetchData();
console.log("Got:", res);
const final = res + " processed";
console.log("Final:", final);
} catch (err) {
console.error("Error:", err);
}
}
main();
是不是 async/await
写法更像同步流程?
Promise 的使用场景 #
一、顺序执行(串行) #
有时候异步任务需要 一个接一个执行,前一个结果传递给下一个:
用 Promise.then
#
function task1(): Promise<number> {
return Promise.resolve(1);
}
function task2(n: number): Promise<number> {
return Promise.resolve(n + 1);
}
task1()
.then(res => task2(res))
.then(result => console.log("串行结果:", result)); // 输出 2
用 async/await
#
async function run() {
const res1 = await task1();
const res2 = await task2(res1);
console.log("串行结果:", res2);
}
run();
二、并行执行 #
多个任务可以同时进行,等待所有完成:
const t1 = Promise.resolve("A");
const t2 = Promise.resolve("B");
Promise.all([t1, t2]).then(([a, b]) => {
console.log("并行结果:", a, b); // "A B"
});
⚡ 适合:同时请求多个接口。
⚠ 注意:只要有一个失败,整个 Promise.all
就会 reject。
三、竞速执行(取最快的) #
如果只关心第一个完成的结果,用 Promise.race
:
function delay(ms: number, value: string) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
Promise.race([delay(1000, "A"), delay(500, "B")])
.then(result => console.log("最快的结果:", result)); // "B"
⚡ 适合:多个数据源取最快响应。
四、所有结果(无论成功失败) #
有时候我们需要 收集所有任务结果,不管它们成功还是失败,用 Promise.allSettled
:
const tasks = [
Promise.resolve("ok"),
Promise.reject("error")
];
Promise.allSettled(tasks).then(results => {
results.forEach(r => console.log(r.status, r.value ?? r.reason));
});
// 输出:fulfilled ok
// rejected error
⚡ 适合:批量任务统计。
五、错误处理与重试 #
普通错误处理 #
fetch("https://api.example.com")
.then(res => res.json())
.catch(err => console.error("请求失败:", err));
重试机制(常见模式) #
function retry<T>(fn: () => Promise<T>, times: number): Promise<T> {
return fn().catch(err => {
if (times <= 0) throw err;
return retry(fn, times - 1);
});
}
retry(() => fetch("https://api.example.com"), 3)
.then(res => console.log("成功"))
.catch(err => console.error("失败:", err));
⚡ 适合:网络请求可能临时失败的情况。
六、超时控制 #
Promise 自带没有超时机制,可以自己封装:
function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
return Promise.race([
p,
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("超时")), ms)
)
]);
}
withTimeout(fetch("https://api.example.com"), 2000)
.then(res => console.log("完成"))
.catch(err => console.error("失败:", err.message));
⚡ 适合:请求卡住时强制结束。
七、控制并发数 #
比如要下载 100 个文件,但不能同时开 100 个请求。可以用队列控制:
async function limitConcurrency<T>(
tasks: (() => Promise<T>)[],
limit: number
): Promise<T[]> {
const results: T[] = [];
const executing: Promise<void>[] = [];
for (const task of tasks) {
const p = task().then(r => results.push(r));
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
executing.splice(executing.findIndex(e => e === p), 1);
}
}
await Promise.all(executing);
return results;
}
⚡ 适合:批量下载 / 批量爬取数据。
八、总结 #
顺序执行 →
.then
链 /await
并行执行 →
Promise.all
竞速执行 →
Promise.race
所有结果 →
Promise.allSettled
错误重试 → 封装 retry
超时控制 →
Promise.race + setTimeout
控制并发 → 手写队列 / 使用第三方库(如 p-limit)