1. 异步编程三部曲的演进与要点
1.1 回调的工作原理与局限
在 JavaScript 的单线程模型中,异步操作通常通过回调函数来完成。当异步任务结束时,会调用回调函数传递结果,这种模式直观但容易引发 回调地狱,导致代码可读性下降、错误处理困难。要点包括事件循环、任务队列和微任务队列的配合。
回调函数的传递使错误处理变得复杂,因为异常往往需要在回调内捕获后再向上抛出,才能被统一处理,错误传递与分发的复杂性不可忽视,最终会让代码臃肿且易出错。
1.2 回调的痛点与演进方向
为了消除回调层级和错误传导的痛点,出现了 Promise 标准,它通过状态机来封装异步结果,提供链式调用、统一的异常处理和更好的组合能力。
// 回调示例
function fetchData(cb) {setTimeout(() => {cb(null, 'data');}, 1000);
}// 使用回调的结果
fetchData((err, data) => {if (err) {console.error(err);return;}console.log(data);
});
在这个阶段,关键改进点是将异步结果封装为 单一的回调约定,以便后续组合与错误传播变得可控。
2. Promise 的引入与实战要点
2.1 Promise 的核心理念与基本用法
Promise 将异步操作的结果包装成一个对象,它有三种状态:待定、已解决、已拒绝,并通过 then/catch/finally 提供链式调用。正确使用 Promise 能显著提升代码的可读性与错误处理的一致性。
// Promise 的基本用法
function fetchDataPromise() {return new Promise((resolve, reject) => {setTimeout(() => {const ok = true;ok ? resolve('data') : reject(new Error('fail'));}, 1000);});
}fetchDataPromise().then(res => console.log('结果:', res)).catch(err => console.error('错误:', err));
通过将异步逻辑包装在 Promise 构造函数里,错误处理从回调展现为 链式异常传播,并且便于组合。
2.2 Promise 的并行与串行控制
使用 Promise 可以方便地实现并行请求,以提高吞吐量;也可以通过串行链式调用实现严格的执行顺序。核心点在于 并行使用 Promise.all、串行的 then 链以及对错误的集中处理。
// 并行示例:同时触发多个请求
const p1 = fetchDataPromise();
const p2 = fetchDataPromise();Promise.all([p1, p2]).then(results => console.log('并行结果:', results)).catch(err => console.error('并行错误:', err));// 串行示例:前一个完成后再进行下一个
fetchDataPromise().then(res => fetchDataPromise()).then(res2 => console.log('串行结果:', res2)).catch(err => console.error('链式错误:', err));
3. Async/Await 的实战要点
3.1 Async/Await 的基础用法与错误处理
Async/Await 将 Promise 的链式调用变成了看起来像同步代码的结构,但底层仍然是基于 Promise。await 会等待 Promise 状态改变,try/catch 则成为统一的错误处理入口。
// Async/Await 基本用法
async function getData() {try {const data = await fetchDataPromise();console.log('数据:', data);} catch (err) {console.error('获取数据失败:', err);}
}
getData();
这种写法极大提升了代码的可读性,使异步逻辑更接近直觉理解,同时减少了回调地狱与多层回调的嵌套。

3.2 Async/Await 的常见坑与最佳实践
常见坑包括未正确处理并发、忽略 rejected Promise、以及错误被吞没等问题。要点在于 对并发进行合理控制、及时处理 rejected,以及在需要时使用 Promise.allSettled 来聚合多任务的结果。
// 并发控制示例:两次获取并处理结果
async function fetchBoth() {const [a, b] = await Promise.all([fetchDataPromise(), fetchDataPromise()]);return { a, b };
}// 使用 allSettled 来处理部分失败的场景
async function fetchAllSettled() {const results = await Promise.allSettled([fetchDataPromise(), fetchDataPromise()]);results.forEach(r => {if (r.status === 'fulfilled') console.log('成功:', r.value);else console.error('失败:', r.reason);});
}
4. 生产级异步代码的最佳实践与可维护性
4.1 结构化组织异步代码
把异步逻辑划分为明确的职责单元,例如数据获取、数据处理和错误处理,使用 服务层与仓储层分离的架构模式来提升可维护性。
// 例:分层结构
async function loadUser(id) {const user = await userService.getUserById(id); // 服务层const profile = await profileService.enrichUser(user); // 另一个服务return profile;
}
4.2 日志、监控与测试覆盖
对于异步系统,清晰的日志与可观测性至关重要。要点包括 错误上报、执行轨迹、吞吐量统计,以及对关键路径的单元测试与集成测试。
// 简单的日志封装示例
async function safeCall(fn, ...args) {try {const start = Date.now();const res = await fn(...args);console.log(`调用完成,耗时=${Date.now() - start}ms`);return res;} catch (err) {console.error('异步调用失败:', err);throw err;}
}


