JavaScript 异步编程完全指南

为什么需要异步编程

JavaScript 是单线程语言,异步编程让我们可以在等待耗时操作(如网络请求)时,不阻塞主线程的执行。

回调函数

回调函数是最早的异步编程方式。

// 回调函数示例
function fetchData(callback) {
  setTimeout(() => {
    const data = { id: 1, name: 'John' };
    callback(null, data);
  }, 1000);
}

fetchData((error, data) => {
  if (error) {
    console.error(error);
  } else {
    console.log(data);
  }
});

回调地狱

// 嵌套回调导致代码难以维护
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      getMoreData(c, function(d) {
        console.log(d);
      });
    });
  });
});

Promise

Promise 是 ES6 引入的异步编程解决方案,解决了回调地狱问题。

// 创建 Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功');
    } else {
      reject('操作失败');
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(result => {
    console.log(result);
    return '下一个值';
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error);
  })
  .finally(() => {
    console.log('无论成功失败都会执行');
  });

Promise 链式调用

// 使用 Promise 解决回调地狱
getData()
  .then(a => getMoreData(a))
  .then(b => getMoreData(b))
  .then(c => getMoreData(c))
  .then(d => console.log(d))
  .catch(error => console.error(error));

Promise.all 和 Promise.race

// 并行执行多个 Promise
const promises = [
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments')
];

Promise.all(promises)
  .then(([users, posts, comments]) => {
    console.log('所有请求完成');
  })
  .catch(error => {
    console.error('任一请求失败');
  });

// 返回最快完成的 Promise
Promise.race(promises)
  .then(result => {
    console.log('最快的请求完成');
  });

Async/Await

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

// async 函数
async function getUserData() {
  try {
    const response = await fetch('/api/user');
    const user = await response.json();
    return user;
  } catch (error) {
    console.error('获取用户数据失败:', error);
    throw error;
  }
}

// 使用 async 函数
async function init() {
  const user = await getUserData();
  console.log(user);
}

init();

并行执行

async function fetchAllData() {
  // 串行执行(慢)
  // const users = await fetchUsers();
  // const posts = await fetchPosts();
  
  // 并行执行(快)
  const [users, posts] = await Promise.all([
    fetchUsers(),
    fetchPosts()
  ]);
  
  return { users, posts };
}

错误处理

async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }
      console.log(`第 ${i + 1} 次重试...`);
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
}

总结

从回调函数到 Promise 再到 async/await,JavaScript 的异步编程越来越优雅。建议在新项目中优先使用 async/await,它让代码更易读、更易维护。