1. Promise 核心设计原则
1.1 构造与执行阶段
在 JavaScript 的异步编程中,Promise 的核心是把异步操作的结果包装成一个对象,通过 Promise 构造函数传入的执行器立即运行,但实际的回调是在微任务队列中执行。执行阶段分为创建、执行、解析三个阶段,这决定了后续 then 的链式行为和错误传播方式。
为了理解执行过程,需要区分同步执行与异步回调。resolve/reject 改变状态并触发微任务,这使得 .then/.catch 的回调在当前事件循环的尾部执行。
// Promise 基本示例
function delay(ms) {return new Promise((resolve) => setTimeout(resolve, ms));
}delay(500).then(() => {console.log('1 秒后输出');return '结果';
}).then(result => {console.log(result);
});
1.2 链式调用与错误传播
链式调用通过 返回新的 Promise 实现,错误沿链向上传播,直到被捕获。这个机制是工程实践中对异常统一处理的基础。
在设计链式 API 时,常见的模式是 让 then 返回一个新的 Promise,并且对非 Promise 值进行自动解析。
// 链式调用与错误传播示例
new Promise((resolve, reject) => {reject(new Error('失败'));
}).then(() => {// 不会执行console.log('成功');
}).catch(err => {console.error('捕获到错误:', err.message);return '恢复后结果';
}).then(v => console.log('最终结果:', v));
2. Async/Await 引入原理
2.1 事件循环与微任务
Async/Await 将异步代码以“看起来像同步”的方式编写,但底层仍然依赖 Promise。await 表达式等待 Promise 进入就绪态并将其结果作为表达式结果,同时 R 值由事件循环调度,避免阻塞主线程。
理解微任务和宏任务的区别对于性能至关重要。await 后的继续执行作为微任务,这会影响 UI 的流畅性与页面的响应速度。
// Async/Await 核心
async function fetchJson(url) {const res = await fetch(url);return res.json();
}
fetchJson('/api/data').then(console.log);
2.2 生成器与状态机的关系
历史上,Async/Await 通过将 Promise 转换为状态机来实现暂停/恢复的效果。背后是状态机对异步流程的编排,使得开发者无需显式地处理回调。
在工程实践中,可以将异步流程切分为阶段性任务,每个阶段返回 Promise,通过 await 组合完成顺序执行,从而降低回调深度。
// 通过状态机模拟暂停/继续
function step(state) {switch(state){case 'start':return Promise.resolve('阶段一完成').then(v => step('second'));case 'second':return Promise.resolve('阶段二完成');}
}
step('start').then(console.log);
3. 工程实践中的异步模式设计
3.1 并发 vs 并行
理解并发与并行的区别是设计高性能应用的基础。并发是结构化的任务调度,并行是实际的多核执行。在浏览器端,真正的并行通常借助 Web Worker 实现。
在后端 Node.js 场景,利用 Promise.all、Promise.allSettled、Promise.race 等组合可以实现并发协作。要避免杯具式的“抛错后所有任务终止”,应结合错误边界进行健壮处理。
// 并发示例:并发请求
async function loadAll(urls) {const promises = urls.map(u => fetch(u).then(r => r.json()));const results = await Promise.all(promises);return results;
}
3.2 断路与重试策略
网络波动时需要对服务降级和重试进行控制。断路器可以防止雪崩效应,指数退避与限流策略是常用组合。
实现思路包括:在固定时间窗内统计失败率,达到阈值后关闭该资源,等待半开尝试后再决定下步行动。
// 简单断路器示例(伪实现)
class CircuitBreaker {constructor(threshold = 0.5, window = 10) {this.threshold = threshold;this.window = window;this.failures = 0;}call(fn) {if (this.failures / this.window > this.threshold) {return Promise.reject(new Error('Circuit open'));}return fn().catch(() => {this.failures++;throw});}
}
3.3 资源管理与取消
取消操作在异步流程中至关重要,AbortController 提供了标准化的取消机制,可以与 fetch、setTimeout、自定义 Promise 组合使用。
在工程设计中,应实现可观测的取消路径,避免悬空请求导致的内存泄漏,并且对取消行为进行日志记录和指标上报。
// AbortController 用法示例
const controller = new AbortController();
fetch('/api/resource', { signal: controller.signal }).then(res => res.json()).catch(err => {if (err.name === 'AbortError') console.log('请求已取消');});
// 取消
controller.abort();
4. 性能优化与异常处理策略
4.1 错误处理与日志
错误处理的策略直接影响用户体验和可维护性。集中式异常捕获与分级日志记录可以快速定位问题。在生产环境应避免吞噬错误,要提供可观测的异常信息。

结合追踪系统,将 Promise 链和 Async/Await 的上下文绑定到请求标识,可在分布式系统中追踪调用链路。
// 全局错误处理示例
window.addEventListener('unhandledrejection', event => {console.error('Unhandled rejection:', event.reason);
});
4.2 请求降级与缓存
对不必实时返回的数据,可以实现缓存和降级策略,缓存命中率直接影响页面响应速度,也降低对后端的压力。
异步请求的缓存策略常见于 短期缓存、版本化键、基于时间的失效,结合本地存储或内存缓存实现。
// 简单缓存的异步请求
const cache = new Map();
async function getData(key, fetcher) {if (cache.has(key)) return cache.get(key);const v = await fetcher();cache.set(key, v);return v;
}


