前言

在JavaScript开发中,异步编程是绕不开的话题。无论是网络请求、文件读取还是定时器操作,都涉及异步处理。早期我们用回调函数,后来有Promise,但这些方式往往让代码变得复杂(想想“回调地狱”)。幸好,ES2017引入了async/await,它让异步代码看起来像同步代码一样简洁易读。

如果你是前端小白或后端Node.js开发者,看到asyncawait就头疼?别慌!这篇文章从零基础入手,带你一步步掌握async/await的使用方法,包括语法、原理、错误处理和实战示例。读完后,你就能自信地在项目中使用它了。文章基于JavaScript最新规范(截至2025年),结合实际代码,纯干货!

什么是async/await?

async/await是基于Promise的语法糖(简化版),用于处理异步操作。

  • async:一个关键字,用于声明异步函数。加了async的函数会自动返回一个Promise对象,即使你没显式返回。
  • await:只能在async函数内部使用,暂停函数执行,等待Promise解析(resolved)或拒绝(rejected)。它让代码“看起来”同步,但实际是异步非阻塞的。

简单说:async创建异步容器,await等待结果。为什么需要它?因为JavaScript是单线程的,异步避免了阻塞主线程,提高性能。

基本语法:怎么用?

1. 定义async函数

// 基本async函数
async function fetchData() {
  // 函数体可以包含异步操作
  return "数据已获取";
}

// 调用它,返回Promise
fetchData().then(result => console.log(result)); // 输出:数据已获取
  • 注意async函数总是返回Promise,所以用.then()或另一个await处理结果。

2. 使用await等待Promise

await只能在async函数里用!它等待一个Promise对象。

// 模拟异步操作(用setTimeout包装Promise)
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function example() {
  console.log("开始");
  
  await delay(1000);  // 等待1秒
  console.log("1秒后");
  
  await delay(2000);  // 再等2秒
  console.log("3秒后结束");
  
  return "完成";
}

// 调用
example().then(console.log);  // 输出顺序:开始 -> 1秒后 -> 3秒后结束 -> 完成
  • 执行流程await暂停当前函数,但不阻塞全局代码。其他代码继续运行。

3. 外层必须是async

如果你想在函数里用await,外层必须是async

// 错误示例:会报错 "SyntaxError: await is only valid in async functions"
function wrong() {
  await somePromise();  // ❌ 不能这样
}

// 正确:包裹在async里
async function right() {
  await somePromise();  // ✅ OK
}

与Promise的对比:为什么async/await更好?

Promise用链式调用,代码容易嵌套;async/await用线性写法,更易维护。

方面 Promise 示例 async/await 示例
基本用法 fetch().then(res => res.json()).then(data => console.log(data)) const data = await (await fetch()).json(); console.log(data);
多步操作 链式长,易出错 顺序写,像同步代码
错误处理 .catch()在链尾 try-catch包裹整个块
可读性 中等

总之,async/await是Promise的“友好版”,推荐在现代JS项目中使用。

错误处理:别忽略try-catch

异步也可能出错(如网络失败),用try-catch捕获。

async function riskyOperation() {
  try {
    const result = await somePromiseThatMightFail();
    console.log(result);
  } catch (error) {
    console.error("出错了:", error.message);
    // 可以返回默认值或重试
    return "默认数据";
  } finally {
    console.log("无论成功失败都执行");
  }
}
  • 为什么用try-catch?因为await会将Promise的reject抛出成异常,正好被catch捕获。
  • 全局错误:如果不处理,错误会传播到外层Promise的.catch()

实战示例:结合Axios发送请求

假设你用Axios库(npm install axios)调用API,这是最常见的场景。

import axios from 'axios';  // ES模块导入

async function getUserList() {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    console.log('用户列表:', response.data);  // 输出用户数组
    return response.data;
  } catch (error) {
    console.error('请求失败:', error.response?.status || error.message);
    throw error;  // 向上抛出,让调用者处理
  }
}

// 调用
getUserList()
  .then(users => {
    users.forEach(user => console.log(user.name));
  })
  .catch(err => console.log('最终错误:', err));
  • 步骤await axios.get()等待响应,response.data就是JSON数据。
  • 优势:代码简洁,像同步读文件一样。

高级用法:并行执行与循环

并行:Promise.all + await

多个异步并行执行,用Promise.all

async function parallelTasks() {
  const [data1, data2] = await Promise.all([
    axios.get('url1'),
    axios.get('url2')
  ]);
  console.log(data1.data, data2.data);
}

循环:for…of + await

顺序处理数组:

async function processItems(items) {
  for (const item of items) {
    const result = await processItem(item);  // 逐个等待
    console.log(result);
  }
}
  • 注意:用for...of而非forEach,因为forEach不等await。

常见坑与最佳实践

  1. 坑1:忘记async,导致await报错。:始终检查外层函数。
  2. 坑2:await后不处理结果。:赋值变量或return。
  3. 坑3:顶层await(ES2022+)。在模块顶层直接用await,但需浏览器/Node支持。
  4. 实践
    • 小项目用原生fetch,大项目用axios。
    • 结合TypeScript,加类型安全。
    • 测试异步:用Jest的async测试函数。

结语

async/await是现代JavaScript异步编程的核心,让代码更优雅、更易调试。从基本语法到实战,你现在应该掌握了!

建议你打开浏览器控制台,复制示例代码运行一遍。遇到问题?欢迎评论区讨论,或参考MDN文档深入学习。

如果这篇文章帮到你,点个赞+收藏,下次我们聊更多JS高级主题!

更多推荐