Skip to content

JavaScript 异步编程学习笔记

js 是单线程,但可以通过异步实现非阻塞。比如网络请求、文件读取这些耗时操作不会阻塞其他代码执行。

回调函数

最早的异步处理方式。

javascript
function fetchData(callback) {
  setTimeout(() => {
    callback({ name: "js" });
  }, 1000);
}

fetchData((data) => {
  console.log(data);
});

回调地狱:

javascript
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      // 嵌套太深了
    });
  });
});

DANGER

回调函数嵌套过深会导致"回调地狱",代码难以阅读和维护。

Promise

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

基本用法

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

promise
  .then((result) => {
    console.log(result); // "操作成功"
  })
  .catch((error) => {
    console.error(error); // "操作失败"
  });

Promise 链式调用

javascript
fetchUserData()
  .then((user) => {
    return fetchUserPosts(user.id);
  })
  .then((posts) => {
    return fetchPostComments(posts[0].id);
  })
  .then((comments) => {
    console.log(comments);
  })
  .catch((error) => {
    console.error("发生错误:", error);
  });

Promise 静态方法

Promise.all()

javascript
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values); // [1, 2, 3]
  })
  .catch((error) => {
    console.error(error);
  });

TIP

Promise.all() 会等待所有 Promise 都成功,如果有一个失败,整个 Promise.all() 就会失败。

Promise.race()

javascript
const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 500));

Promise.race([promise1, promise2])
  .then((value) => {
    console.log(value); // 2 (最先完成的)
  });

Promise.allSettled()

javascript
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("错误");

Promise.allSettled([promise1, promise2])
  .then((results) => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: '错误' }
    // ]
  });

async/await

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

基本用法

javascript
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("获取数据失败:", error);
  }
}

fetchData();

并行执行多个异步操作

javascript
async function fetchMultipleData() {
  try {
    // 并行执行
    const [user, posts, comments] = await Promise.all([
      fetchUser(),
      fetchPosts(),
      fetchComments()
    ]);
    
    console.log(user, posts, comments);
  } catch (error) {
    console.error(error);
  }
}

async/await 与 Promise 结合

javascript
async function processData() {
  const user = await fetchUser();
  const posts = await fetchUserPosts(user.id);
  const comments = await fetchPostComments(posts[0].id);
  
  return { user, posts, comments };
}

processData()
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error(error);
  });

实际应用示例

使用 fetch API

javascript
async function getUserData(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const userData = await response.json();
    return userData;
  } catch (error) {
    console.error("获取用户数据失败:", error);
    throw error;
  }
}

错误处理最佳实践

javascript
async function safeAsyncOperation() {
  try {
    const result = await riskyOperation();
    return { success: true, data: result };
  } catch (error) {
    // 记录错误但不中断程序
    console.error("操作失败:", error);
    return { success: false, error: error.message };
  }
}

总结

  • 回调函数:最基础的异步处理方式,但容易产生回调地狱
  • Promise:解决了回调地狱问题,提供了更好的错误处理
  • async/await:让异步代码更易读,是推荐的现代写法