【JavaScript async/await】一文搞懂异步编程
前言
在JavaScript开发中,异步编程是绕不开的话题。无论是网络请求、文件读取还是定时器操作,都涉及异步处理。早期我们用回调函数,后来有Promise,但这些方式往往让代码变得复杂(想想“回调地狱”)。幸好,ES2017引入了async/await,它让异步代码看起来像同步代码一样简洁易读。
如果你是前端小白或后端Node.js开发者,看到async和await就头疼?别慌!这篇文章从零基础入手,带你一步步掌握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:忘记async,导致await报错。解:始终检查外层函数。
- 坑2:await后不处理结果。解:赋值变量或return。
- 坑3:顶层await(ES2022+)。在模块顶层直接用await,但需浏览器/Node支持。
- 实践:
- 小项目用原生fetch,大项目用axios。
- 结合TypeScript,加类型安全。
- 测试异步:用Jest的
async测试函数。
结语
async/await是现代JavaScript异步编程的核心,让代码更优雅、更易调试。从基本语法到实战,你现在应该掌握了!
建议你打开浏览器控制台,复制示例代码运行一遍。遇到问题?欢迎评论区讨论,或参考MDN文档深入学习。
如果这篇文章帮到你,点个赞+收藏,下次我们聊更多JS高级主题!
更多推荐



所有评论(0)