Skip to content

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 浏览器事件循环模型

Event Loop

关键组件:

  1. 调用栈(Call Stack)
  2. 任务队列(Task Queue)
    • 宏任务(Macrotask):setTimeout、I/O
    • 微任务(Microtask):Promise、MutationObserver
  3. 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 事件循环阶段

  1. Timers(执行 setTimeout 等)
  2. Pending Callbacks(系统级回调)
  3. Idle/Prepare(内部使用)
  4. Poll(检索新I/O事件)
  5. Check(setImmediate)
  6. Close Callbacks(关闭事件)

四、现代异步方案详解

4.1 Promise 高级技巧

// 中断式 Promise
function 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 Workers
const 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 异步编程需要理解:

  1. 事件循环的运行机制
  2. 不同异步方案的适用场景
  3. 内存管理与性能优化
  4. 现代框架的异步整合

面试建议:

  • 深入理解 Event Loop 运行原理
  • 熟练掌握 Promise 高级用法
  • 能实现常见异步模式(重试、超时、并发控制)
  • 关注浏览器 Performance 面板分析技巧

进阶方向:

  • 结合 TypeScript 类型系统
  • 探索 RxJS 响应式编程
  • 学习 WebAssembly 多线程方案