理解 JavaScript 中的 async/await:从基础用法到实战技巧

更新日期: 2025-10-11阅读: 23标签: async

面试官看着你,问道:“可以讲讲 async/await 到底是什么吗?”

这是很多前端开发者面试时都会遇到的问题。不少人第一反应是:“就是让异步代码看起来像同步的……”但这样的回答太浅显了。

面试官想知道的不仅仅是定义,而是你是否真正理解它的工作原理、解决了什么问题,以及在实际开发中需要注意哪些细节。


为什么需要 async/await?

在深入了解之前,我们先看看 JavaScript 异步处理的发展过程。

最早的异步处理使用回调函数

getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getFinalData(c, function(result) {
        console.log(result);
      });
    });
  });
});

这种代码层层嵌套,形成了所谓的“回调地狱”,代码难以阅读和维护。

ES6 引入 Promise 后,情况有所改善:

getData()
  .then(a => getMoreData(a))
  .then(b => getEvenMoreData(b))
  .then(c => getFinalData(c))
  .then(result => console.log(result))
  .catch(err => console.error(err));

Promise 使用链式调用,代码结构更清晰。但在复杂场景下,Promise 仍有不足:

  • 条件判断变得复杂

  • 中间变量传递麻烦

  • 错误处理不够直观

  • 循环处理异步操作很绕

async/await 的出现解决了这些问题,让我们能用同步的写法处理异步逻辑。


async/await 基础用法

先看一个简单例子:

async function fetchData() {
  try {
    const response = await fetch('/api/user');
    const user = await response.json();
    console.log(user);
  } catch (error) {
    console.error('请求出错:', error);
  }
}

这里有三个关键点需要掌握:

  1. async 函数总是返回 Promise

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

hello().then(console.log); // 输出 'world'

如果 async 函数返回一个值,会自动包装成 Promise。如果抛出错误,会变成拒绝的 Promise。

  1. await 只能在 async 函数内部使用

在普通函数中使用 await 会导致语法错误。

  1. await 可以等待任何值

如果 await 后面的不是 Promise,会自动转换成 Promise:

const num = await 42; // 等同于 await Promise.resolve(42)


理解 async/await 的工作原理

很多人误以为 await 会阻塞线程,其实不是。await 只是把后续代码注册为微任务,等当前同步代码执行完毕后再执行。

看这个例子:

console.log('1');

async function foo() {
  console.log('2');
  await Promise.resolve();
  console.log('3');
}

foo();
console.log('4');

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

await Promise.resolve() 把 console.log('3') 放入微任务队列,先执行完同步代码 console.log('4'),再从微任务队列中取出执行。


实际开发中的常见用法

  1. 并行执行异步操作

错误做法(串行执行):

async function slowExample() {
  const a = await fetchA(); // 等待 200ms
  const b = await fetchB(); // 再等待 200ms
  const c = await fetchC(); // 再等待 200ms
  // 总耗时约 600ms
}

正确做法(并行执行):

async function fastExample() {
  const [a, b, c] = await Promise.all([
    fetchA(),
    fetchB(),
    fetchC()
  ]);
  // 总耗时约 200ms
}

如果需要处理可能失败的请求,可以使用 Promise.allSettled:

const results = await Promise.allSettled([
  fetchA(),
  fetchB(),
  fetchC()
]);

results.forEach((result) => {
  if (result.status === 'fulfilled') {
    console.log('成功:', result.value);
  } else {
    console.log('失败:', result.reason);
  }
});
  1. 条件判断中的异步操作

async function loadPage() {
  const user = await fetchUser();
  
  if (user.isAdmin) {
    const data = await fetchAdminData();
    renderAdminPage(data);
  } else {
    const data = await fetchUserData();
    renderUserPage(data);
  }
}

这种条件分支的异步处理,用 async/await 写起来很直观。

  1. 循环中的异步处理

常见错误:

async function slowLoop() {
  const ids = [1, 2, 3, 4, 5];
  for (let id of ids) {
    await fetchUser(id); // 串行执行,效率低
  }
}

改进方法:如果需要并行执行:

async function parallelLoop() {
  const ids = [1, 2, 3, 4, 5];
  await Promise.all(ids.map(id => fetchUser(id)));
}

如果需要顺序执行:

async function sequentialLoop() {
  const ids = [1, 2, 3, 4, 5];
  for (let id of ids) {
    const user = await fetchUser(id);
    process(user); // 确保顺序处理
  }
}


常见陷阱和解决方法

  1. 忘记错误处理

async function unsafeFetch() {
  const res = await fetch('/api/data');
  return res.json();
}
// 如果请求失败,错误无法被捕获

正确做法:

async function safeFetch() {
  try {
    const res = await fetch('/api/data');
    const data = await res.json();
    return data;
  } catch (error) {
    console.error('请求失败:', error);
    return null;
  }
}
  1. 在数组方法中误用 await

const urls = ['/a', '/b', '/c'];

urls.map(async (url) => {
  const res = await fetch(url);
  console.log(await res.text());
});

console.log('done'); // 这行会先执行

map 方法不会等待 async 函数执行完成。应该使用 for...of 循环:

for (let url of urls) {
  const res = await fetch(url);
  console.log(await res.text());
}
console.log('done'); // 等待所有请求完成


高级应用场景

  1. 实现重试机制

async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (error) {
      if (i === retries - 1) throw error;
      // 等待时间逐渐增加
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}
  1. 超时控制

async function fetchWithTimeout(url, timeout = 5000) {
  const fetchPromise = fetch(url);
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('请求超时')), timeout);
  });
  
  return await Promise.race([fetchPromise, timeoutPromise]);
}
  1. 处理数据

async function processStream(stream) {
  for await (const chunk of stream) {
    await processChunk(chunk);
  }
}


总结

async/await 让异步代码更易读写,但理解其底层原理很重要。记住几个关键点:

  • async 函数总是返回 Promise

  • await 把后续代码放入微任务队列

  • 注意错误处理,使用 try/catch

  • 合理选择串行或并行执行

  • 避免在数组方法中误用 await

掌握这些知识,不仅能通过面试,更能写出健壮的异步代码。在实际项目中,根据具体需求选择合适的异步处理方式,才能发挥 async/await 的最大价值。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/12985

JS中的async/await的执行顺序详解

虽然大家知道async/await,但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 JavaScript 的 async/await

ES6新特性Async

Async实际上是一个封装了自动化执行并返回一个Promise的Generator函数的语法糖。这句话的意思我们可以分为三个部分来解读:首先它有一个自动化执行,Generator函数是依靠不停的调用.net来依次执行的,Async有一个自动化执行的过程

Node.js Async 函数最佳实践

Node.js7.6起, Node.js 搭载了有async函数功能的V8引擎。当Node.js 8于10月31日成为LTS版本后,我们没有理由不使用async函数。接下来,我将简要介绍async函数,以及如何改变我们编写Node.js应用程序的方式。

将async/await编译到 ES3/ES5 (外部帮助库)

自2015年11 发布1.7版以来,TypeScript 已支持 async/await 关键字。编译器使用 yield 将异步函数转换为生成器函数。这意味着咱们无法针对 ES3 或 ES5,因为生成器仅在 ES6 中引入的。

你可能忽略的 async/await 问题

async/await 大家肯定都用过,在处理异步操作的时候真的是很方便。那今天主要讲一些在使用 async/await 时容易忽略和犯错的地方。上面的代码中,每一行都会 等待上一行的结果返回后才会执行。

使用async await通过for循环在图片onload加载成功后获取成功的图片地址

有一个图片列表,我想要在图片onload成功之后获取加载成功的图片列表,图片资源加载为异步,我们使用ES7的async await方式实现,多张图片,是用for循环。

ES8 Async 和 Await

Async 和 Awaiit 是 Promise 的扩展,我们知道 JavaScript 是单线程的,使用 Promise 之后可以使异步操作的书写更简洁,而 Async 使 Promise 像同步操作

有了 Promise 和 then,为什么还要使用 async?

最近代码写着写着,我突然意识到一个问题——我们既然已经有了 Promise 和 then,为啥还需要 async 和 await?这不是脱裤子放屁吗?

手写async await的最简实现

如果让你手写async函数的实现,你是不是会觉得很复杂?这篇文章带你用20行搞定它的核心。经常有人说async函数是generator函数的语法糖,那么到底是怎么样一个糖呢?让我们来一层层的剥开它的糖衣。

认识async/await

async/await是ES7的写法,可以让非同步call back写法看起来像同步的顺序去执行。以下我们new一个Promise的class并return给一个function

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!