异步编程的演进

JavaScript 的异步编程经历了几个阶段的演进:

回调函数时代

// 获取用户信息,然后获取订单
getUser(function(user) {
  getOrders(user.id, function(orders) {
    console.log(orders);
  });
});

问题:回调地狱,代码难以维护。

Promise 时代

getUser()
  .then(user => getOrders(user.id))
  .then(orders => console.log(orders))
  .catch(err => console.error(err));

问题:链式调用虽然改善了回调地狱,但仍然不够直观。

async/await 时代

async function fetchUserOrders() {
  try {
    const user = await getUser();
    const orders = await getOrders(user.id);
    console.log(orders);
  } catch (err) {
    console.error(err);
  }
}

async/await 让异步代码看起来像同步代码,更加直观易读。

async/await 基础

async 函数

async 函数总是返回一个 Promise:

async function foo() {
  return 'hello';
}

foo().then(console.log); // 'hello'

// 等价于
function foo() {
  return Promise.resolve('hello');
}

await 表达式

await 会暂停函数执行,等待 Promise 解决:

async function example() {
  const result = await Promise.resolve('done');
  console.log(result); // 'done'
}

常见用法

顺序执行

async function sequential() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(2);
  const c = await Promise.resolve(3);
  return a + b + c; // 6
}

并行执行

async function parallel() {
  const [a, b, c] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3)
  ]);
  return a + b + c; // 6
}

错误处理

async functionWithErrorHandling() {
  try {
    const result = await riskyOperation();
    return result;
  } catch (error) {
    console.error('出错了:', error);
    // 可以选择:
    // 1. 返回默认值
    return null;
    // 2. 抛出新错误
    throw new Error('处理失败');
    // 3. 返回错误信息
    return { error: true, message: error.message };
  }
}

最佳实践

  1. 始终使用 try/catch:处理可能的错误
  2. 善用 Promise.all:并行执行独立的异步操作
  3. 避免过度 await:不需要等待的就不要 await
  4. 循环中使用 Promise.all:而不是 for 循环加 await
// 不推荐
for (const item of items) {
  await processItem(item);
}

// 推荐
await Promise.all(items.map(item => processItem(item)));

总结

async/await 是 JavaScript 异步编程的重大改进,它让异步代码更加简洁易读。掌握 async/await 是每个前端开发者的必备技能。