JavaScript 异步编程全面解析
一、异步编程的必要性
1.1 单线程模型限制
JavaScript 采用单线程执行模型,为避免阻塞需要异步处理:
// 同步阻塞示例const data = syncReadFile(); // 阻塞后续代码console.log(data);
// 异步非阻塞示例asyncReadFile(data => { console.log(data);});console.log('继续执行'); // 立即输出
1.2 典型异步场景
- 网络请求(AJAX/Fetch)
- 文件I/O(Node.js)
- 定时操作(setTimeout)
- 用户交互事件
- 动画帧处理(requestAnimationFrame)
二、异步编程演进路线
2.1 回调函数时代
// 回调地狱示例getUser(userId, function(user) { getOrders(user.id, function(orders) { getProducts(orders[0].id, function(product) { renderProduct(product); }); });});
痛点:回调金字塔、错误处理困难、流程控制复杂
2.2 Promise 革命
// Promise 链式调用getUser(userId) .then(user => getOrders(user.id)) .then(orders => getProducts(orders[0].id)) .then(renderProduct) .catch(handleError);
// 静态方法Promise.all([api1(), api2()]).then(results => {});Promise.race([timeout(500), fetchData()]);
2.3 Generator + Co 方案
function* gen() { const user = yield getUser(); const orders = yield getOrders(user.id); return orders;}
co(gen).then(orders => {});
2.4 Async/Await 终极方案
async function getData() { try { const user = await getUser(); const orders = await getOrders(user.id); return orders; } catch (error) { handleError(error); }}
三、事件循环核心机制
3.1 浏览器事件循环模型
关键组件:
- 调用栈(Call Stack)
- 任务队列(Task Queue)
- 宏任务(Macrotask):setTimeout、I/O
- 微任务(Microtask):Promise、MutationObserver
- Web APIs 环境
3.2 执行顺序规则
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => { console.log('3'); // 微任务});
console.log('4');
// 输出顺序:1 → 4 → 3 → 2
3.3 Node.js 事件循环阶段
- Timers(执行 setTimeout 等)
- Pending Callbacks(系统级回调)
- Idle/Prepare(内部使用)
- Poll(检索新I/O事件)
- Check(setImmediate)
- Close Callbacks(关闭事件)
四、现代异步方案详解
4.1 Promise 高级技巧
// 中断式 Promisefunction cancellable(promise) { let cancel; const wrapped = new Promise((resolve, reject) => { cancel = reject; promise.then(resolve).catch(reject); }); return [wrapped, cancel];}
// 进度通知function withProgress(fn) { return (...args) => { const progress = new EventEmitter(); const promise = fn(...args, p => progress.emit('progress', p)); return Object.assign(promise, { progress }); };}
4.2 Async 函数原理
// async/await 转换为生成器async function example() { /* ... */ }// 等价于function example() { return spawn(function*() { // yield 转换为 await });}
五、常见异步场景实践
5.1 并发控制
// 限制并发数async function parallel(tasks, concurrency = 5) { const results = []; const executing = new Set();
for (const task of tasks) { const p = task().finally(() => executing.delete(p)); executing.add(p); results.push(p);
if (executing.size >= concurrency) { await Promise.race(executing); } } return Promise.all(results);}
5.2 超时处理
function withTimeout(promise, timeout) { return Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ) ]);}
六、性能陷阱与优化
6.1 常见问题
- 微任务堆积:过度使用 Promise 导致界面卡顿
- 内存泄漏:未释放事件监听器
- 阻塞事件循环:长时间同步操作
6.2 优化策略
// 分解长任务async function processChunks(data) { for (let i = 0; i < data.length; i += 100) { const chunk = data.slice(i, i + 100); await Promise.resolve().then(() => process(chunk)); }}
// 使用 Web Workersconst worker = new Worker('task.js');worker.postMessage(data);worker.onmessage = e => handleResult(e.data);
七、面试高频问题解析
7.1 经典输出题
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve() .then(() => console.log('promise1')) .then(() => console.log('promise2'));
requestAnimationFrame(() => console.log('raf'));
console.log('end');
/* 输出顺序:start → end → promise1 → promise2 → raf → timeout*/
7.2 手写 Promise
class MyPromise { constructor(executor) { // 实现 pending/fulfilled/rejected 状态转换 // 实现 then 方法链式调用 // 处理异步 resolve }}
八、现代应用结合
8.1 React Hooks 中的异步
useEffect(() => { let isMounted = true; fetchData().then(data => { if (isMounted) setState(data); }); return () => isMounted = false;}, []);
8.2 Node.js Stream 处理
readableStream .pipe(transformStream) .on('data', chunk => process(chunk)) .on('end', () => console.log('Complete'));
总结
掌握 JavaScript 异步编程需要理解:
- 事件循环的运行机制
- 不同异步方案的适用场景
- 内存管理与性能优化
- 现代框架的异步整合
面试建议:
- 深入理解 Event Loop 运行原理
- 熟练掌握 Promise 高级用法
- 能实现常见异步模式(重试、超时、并发控制)
- 关注浏览器 Performance 面板分析技巧
进阶方向:
- 结合 TypeScript 类型系统
- 探索 RxJS 响应式编程
- 学习 WebAssembly 多线程方案