一、Promise 基础概念
Promise 是 JavaScript 中处理异步操作的核心对象,代表一个异步操作的最终完成或失败及其结果值。它解决了传统回调函数(Callback)带来的”回调地狱”问题,使异步代码更加可读、可维护。
三种状态
| 状态 |
说明 |
pending |
初始状态,既未成功也未失败 |
fulfilled |
操作成功完成 |
rejected |
操作失败 |
状态一旦从 pending 转变为 fulfilled 或 rejected,就不可逆转。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const promise = new Promise((resolve, reject) => { setTimeout(() => { const success = true; if (success) { resolve('操作成功'); } else { reject(new Error('操作失败')); } }, 1000); });
promise .then(result => console.log(result)) .catch(err => console.error(err.message));
|
二、Promise 核心方法
1. .then() 链式调用
1 2 3 4
| fetchUser(1) .then(user => fetchOrders(user.id)) .then(orders => fetchOrderDetail(orders[0].id)) .then(detail => console.log(detail));
|
每个 .then() 返回一个新的 Promise,支持链式调用。
2. .catch() 错误处理
1 2 3 4 5 6 7 8
| asyncOperation() .then(result => { }) .catch(error => { console.error('出错了:', error); });
|
3. .finally() 最终执行
1 2 3 4
| showLoading() .then(data => render(data)) .catch(err => showError(err)) .finally(() => hideLoading());
|
三、Promise 静态方法
Promise.all — 全部成功才算成功
1 2 3 4 5 6
| const [user, orders, products] = await Promise.all([ fetchUser(), fetchOrders(), fetchProducts(), ]);
|
Promise.allSettled — 等待所有结果(ES2020)
1 2 3 4 5
| const results = await Promise.allSettled([ fetchWithRetry(), fetchBackup(), ]);
|
Promise.race — 取第一个完成的结果
1 2 3 4 5 6
| const winner = await Promise.race([ fetchFromCDN(), fetchFromOrigin(), timeout(5000), ]);
|
Promise.any — 取第一个成功的结果(ES2021)
1 2 3 4 5 6 7
| const first = await Promise.any([ fetchFromServerA(), fetchFromServerB(), fetchFromServerC(), ]);
|
四、实用技巧
1. 串行执行数组中的异步任务
1 2 3 4 5 6 7
| async function sequentialTasks(tasks) { const results = []; for (const task of tasks) { results.push(await task()); } return results; }
|
2. 并发限制(控制同时执行的 Promise 数量)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| async function promisePool(tasks, concurrency) { const results = new Array(tasks.length); let index = 0;
async function worker() { while (index < tasks.length) { const i = index++; results[i] = await tasks[i](); } }
const workers = Array.from({ length: concurrency }, worker); await Promise.all(workers); return results; }
const files = list.map(file => () => uploadFile(file)); await promisePool(files, 3);
|
3. 带超时的 Promise
1 2 3 4 5 6 7 8
| function withTimeout(promise, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms) ); return Promise.race([promise, timeout]); }
const data = await withTimeout(fetchUser(1), 3000);
|
4. 重试机制
1 2 3 4 5 6 7 8 9 10 11 12
| async function retry(fn, times = 3, delay = 1000) { for (let i = 0; i < times; i++) { try { return await fn(); } catch (err) { if (i === times - 1) throw err; await new Promise(r => setTimeout(r, delay * (i + 1))); } } }
const result = await retry(() => unstableAPI(), 3, 500);
|
5. Promise 化回调函数(promisify)
1 2 3 4 5
| const { promisify } = require('util'); const fs = require('fs');
const readFileAsync = promisify(fs.readFile); const content = await readFileAsync('./data.txt', 'utf-8');
|
五、async/await 最佳实践
1. 错误处理:try/catch
1 2 3 4 5 6 7 8 9
| async function getUser() { try { const res = await fetch('/api/user'); const data = await res.json(); return data; } catch (err) { console.error('获取用户失败:', err); } }
|
2. 避免不必要的串行等待
1 2 3 4 5 6 7 8 9
| const user = await fetchUser(); const posts = await fetchPosts();
const [user, posts] = await Promise.all([ fetchUser(), fetchPosts(), ]);
|
3. 顶层 await(Node.js 14.8+ ESM)
1 2 3
| const config = await loadConfig(); export default config;
|
六、面试常见问题
Q1:Promise 和 Callback 有什么区别?
| 对比项 |
Callback |
Promise |
| 可读性 |
嵌套深,回调地狱 |
链式调用,扁平化 |
| 错误处理 |
每个回调单独处理 |
.catch() 统一捕获 |
| 组合能力 |
难以组合多个异步操作 |
all/race/any 等原生支持 |
| 状态管理 |
无法保证只调用一次 |
settle 后不可变 |
Q2:.then() 和 .catch() 的执行顺序是怎样的?
1 2 3 4
| Promise.reject('err') .then(() => console.log('A')) .catch(() => console.log('B')) .then(() => console.log('C'));
|
.catch() 相当于 .then(null, onRejected),捕获前面的错误后,后续 .then() 正常执行。
Q3:如何手动实现一个 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
| class MyPromise { constructor(executor) { this.state = 'pending'; 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 (err) { reject(err); } }
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
const promise2 = new MyPromise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { try { const x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (this.state === 'rejected') { setTimeout(() => { try { const x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else { this.onFulfilledCallbacks.push(() => { }); this.onRejectedCallbacks.push(() => { }); } });
return promise2; } }
function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) return reject(new TypeError('Chaining cycle detected')); if (x && (typeof x === 'object' || typeof x === 'function')) { let called = false; try { const then = x.then; if (typeof then === 'function') { then.call(x, y => { if (!called) { called = true; resolve(y); } }, r => { if (!called) { called = true; reject(r); } }); } else { resolve(x); } } catch (e) { if (!called) reject(e); } } else { resolve(x); } }
|
Q4:Promise.all 和 Promise.allSettled 的区别?
Promise.all:快速失败,任意一个 reject 则整体立即 reject。
Promise.allSettled:全部等待,不论成功失败都返回完整结果数组。
适用场景:all 用于必须全部成功的并行任务;allSettled 用于希望获取所有结果的场景(如批量上报)。
Q5:async 函数返回值是什么?
async 函数始终返回一个 Promise。
- 如果函数
return value,返回的 Promise 以 value resolve。
- 如果函数
throw error,返回的 Promise 以 error reject。
- 如果
return 一个 Promise,会等待该 Promise settle。
1 2
| async function foo() { return 42; } foo().then(v => console.log(v));
|
Q6:如何捕获 async 函数中未处理的异常?
1 2 3 4 5 6 7 8
|
asyncFn().catch(err => log(err));
process.on('unhandledRejection', (reason) => { console.error('Unhandled Rejection:', reason); });
|
Q7:Promise 的微任务(Microtask)机制
1 2 3 4 5 6 7 8 9
| console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
|
规则:同步代码 > 微任务(Promise.then) > 宏任务(setTimeout)。
七、常见陷阱
1. 循环中使用 await
1 2 3 4 5 6 7
| for (const url of urls) { const res = await fetch(url); }
const results = await Promise.all(urls.map(url => fetch(url)));
|
2. 忘记 return 或 await
1 2 3 4 5 6 7 8 9
| function getData() { fetch('/api').then(res => res.json()); }
function getData() { return fetch('/api').then(res => res.json()); }
|
3. resolve 一个 Promise
1 2 3 4
| new Promise(resolve => { resolve(Promise.reject('err')); });
|
总结
| 场景 |
推荐方案 |
| 单个异步操作 |
async/await + try/catch |
| 多个并行独立操作 |
Promise.all |
| 需要全部结果(含失败) |
Promise.allSettled |
| 只要最快成功 |
Promise.any |
| 超时控制 |
Promise.race + timeout |
| 并发限制 |
自实现 promisePool |
掌握 Promise 是 Node.js 异步编程的基石,无论是日常开发还是面试,理解其底层机制(状态机、微任务队列、链式调用原理)都能让你游刃有余。