JavaScript 异步编程入门

2026-06-22 · 6 阅读 · 291字
JavaScript

JavaScript 异步编程入门

为什么要异步

JavaScript 是单线程语言,意味着同一时间只能执行一个任务。如果有耗时操作(如网络请求、文件读取),会阻塞后续代码执行。异步编程解决了这个问题:让耗时操作在后台执行,完成后通过回调通知主线程。

回调函数(Callback)

回调是最早的异步模式。将函数作为参数传递给异步操作,操作完成后调用该函数。

function fetchData(callback) {
  setTimeout(() => {
    callback('数据加载完成');
  }, 1000);
}

fetchData((data) => {
  console.log(data); // 1秒后输出
});

回调地狱:当多个异步操作嵌套时,代码变成金字塔状,难以阅读和维护。

getUser(id, (user) => {
  getPosts(user.id, (posts) => {
    getComments(posts[0].id, (comments) => {
      // 嵌套越来越深
    });
  });
});

Promise

Promise 是一种更优雅的异步方案。它有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)。

const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('数据加载完成');
    } else {
      reject('加载失败');
    }
  }, 1000);
});

fetchData
  .then((data) => console.log(data))
  .catch((error) => console.error(error));

Promise 链式调用

getUser(id)
  .then((user) => getPosts(user.id))
  .then((posts) => getComments(posts[0].id))
  .then((comments) => console.log(comments))
  .catch((error) => console.error(error));

并发控制

// 全部完成
Promise.all([fetchUsers(), fetchPosts(), fetchComments()])
  .then(([users, posts, comments]) => { ... });

// 任意一个完成
Promise.race([fetchData(), timeout(5000)])
  .then((data) => console.log(data))
  .catch(() => console.error('请求超时'));

// 全部完成(含失败)
Promise.allSettled([fetchUsers(), fetchPosts()])
  .then((results) => results.forEach(r => console.log(r.status)));

async/await

async/await 是 Promise 的语法糖,让异步代码看起来像同步代码。

async function loadData() {
  try {
    const user = await getUser(id);
    const posts = await getPosts(user.id);
    const comments = await getComments(posts[0].id);
    return comments;
  } catch (error) {
    console.error('加载失败:', error);
  }
}

并行请求

async function loadDashboard() {
  const [users, posts, comments] = await Promise.all([
    fetchUsers(),
    fetchPosts(),
    fetchComments()
  ]);
  return { users, posts, comments };
}

事件循环(Event Loop)

理解事件循环是掌握异步的关键。JS 执行顺序:

  1. 执行同步代码(调用栈)
  2. 遇到微任务(Promise.then、MutationObserver)→ 放入微任务队列
  3. 遇到宏任务(setTimeout、setInterval、I/O)→ 放入宏任务队列
  4. 同步代码执行完毕 → 清空微任务队列
  5. 取出一个宏任务执行 → 重复第 4 步
console.log('1'); // 同步

setTimeout(() => console.log('2'), 0); // 宏任务

Promise.resolve().then(() => console.log('3')); // 微任务

console.log('4'); // 同步

// 输出顺序:1, 4, 3, 2

总结

方式 优点 缺点
回调 简单直接 回调地狱,错误处理困难
Promise 链式调用,错误处理统一 理解门槛稍高
async/await 代码简洁,可读性强 需要配合 try/catch

实战中推荐以 async/await 为主,需要并发控制时配合 Promise.all 使用。