太好了!我们来轻松愉快地学懂 async/await,这是 JavaScript 处理异步操作最现代、最优雅的方式。
1. 先看问题:什么是异步?
想象一下你同时做两件事:
- 同步(排队):排队买奶茶,队伍很长,你只能干等着
- 异步(多任务):网上点奶茶,等待时你还能刷手机、工作
异步代码就是:不用等前一个任务完成,就能做下一个任务。
2. 传统方式的问题(回调地狱)
先看看不用 async/await 的代码:
// 场景:你要做三件事:煮面、煎蛋、吃饭
// 传统回调方式(很乱!)
煮面(function() {
煎蛋(function() {
吃饭(function() {
洗碗(function() {
// 更多嵌套...
})
})
})
})
// 这种写法叫"回调地狱"(Callback Hell)
// 代码向右"生长",很难阅读和维护
3. Promise 登场(先理解这个)
Promise 就像"提货券":
-
制作中(pending):奶茶还没做好
-
已完成(fulfilled):奶茶做好了,可以取了
-
已失败(rejected):奶茶打翻了,做不了
// 创建一个Promise(就像拿到一张提货券)
const 煮面Promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('🍜 面煮好了!')
resolve('香喷喷的面条') // 成功时调用
}, 2000)
})// 使用.then()取货
煮面Promise.then((面条) => {
console.log('我拿到了:', 面条)
}).catch((错误) => {
console.log('出错了:', 错误)
})
4. async/await 来了!(更简洁的方式)
async/await 让异步代码看起来像同步代码一样简单!
基本规则:
-
async:声明一个函数是"异步的"
-
await:在 async 函数里,等待一个 Promise 完成
// 1. 用 async 声明异步函数
async function 做饭() {
// 2. 用 await 等待
const 面条 = await 煮面()
const 鸡蛋 = await 煎蛋()console.log(
🎉 可以吃饭了:${面条} + ${鸡蛋})
return '美味的一餐'
}// 调用
做饭().then(结果 => {
console.log(结果)
})
5. 实际例子对比
场景:获取用户信息 → 获取订单 → 显示结果
// 传统 Promise 方式(.then链)
function 获取用户信息() {
获取用户()
.then(用户 => {
return 获取订单(用户.id)
})
.then(订单 => {
return 显示结果(订单)
})
.catch(错误 => {
console.error('出错了', 错误)
})
}
// async/await 方式(简洁明了!)
async function 获取用户信息() {
try {
const 用户 = await 获取用户()
const 订单 = await 获取订单(用户.id)
const 结果 = await 显示结果(订单)
return 结果
} catch (错误) {
console.error('出错了', 错误)
}
}
6. 错误处理(try...catch)
async/await 用普通的 try...catch 处理错误,很简单!
async function 点外卖() {
try {
console.log('📱 打开外卖APP...')
const 菜单 = await 获取菜单() // 可能失败:没网络
const 订单号 = await 下单(菜单[0]) // 可能失败:余额不足
const 外卖 = await 等待送达(订单号) // 可能失败:外卖小哥摔倒了
console.log('🎉 外卖到了!', 外卖)
return 外卖
} catch (错误) {
// 任何一步出错都会到这里
console.log('❌ 点外卖失败:', 错误.message)
if (错误.message.includes('网络')) {
alert('检查一下Wi-Fi?')
} else if (错误.message.includes('余额')) {
alert('余额不足,请充值')
} else {
alert('未知错误,联系客服')
}
}
}
7. 并行执行(提高效率)
有时候不需要等一个完成再做下一个:
// 错误的:这样会很慢(一个一个等)
async function 慢速做饭() {
const 饭 = await 煮饭() // 等20分钟
const 汤 = await 煮汤() // 等15分钟
const 菜 = await 炒菜() // 等10分钟
// 总共:45分钟!
}
// 正确的:一起做(并行)
async function 快速做饭() {
// 同时开始做三件事
const 饭Promise = 煮饭() // 开始煮饭,不等
const 汤Promise = 煮汤() // 开始煮汤,不等
const 菜Promise = 炒菜() // 开始炒菜,不等
// 等所有都完成
const [饭, 汤, 菜] = await Promise.all([
饭Promise,
汤Promise,
菜Promise
])
// 总共:20分钟(按最慢的算)
console.log('🍚 开饭啦!')
}
8. 实战:结合 axios 的 async/await
// 1. 用 async 声明函数
async function 获取用户数据() {
try {
// 2. 用 await 等待请求完成
console.log('🔄 正在获取用户数据...')
// await 会"暂停"在这里,直到请求完成
const 响应 = await axios.get('/api/user/123')
console.log('✅ 获取成功!')
console.log('用户信息:', 响应.data)
// 可以继续发请求(看起来像同步代码)
const 订单 = await axios.get(`/api/orders?userId=${响应.data.id}`)
console.log('用户订单:', 订单.data)
return 订单.data
} catch (错误) {
console.log('❌ 获取失败:', 错误.message)
// 错误处理也很简单
if (错误.response?.status === 404) {
alert('用户不存在!')
}
}
}
// 调用这个函数
async function 页面加载() {
const 订单数据 = await 获取用户数据()
console.log('页面数据:', 订单数据)
}
页面加载()
9. 简单记忆法
// async 就像"魔法咒语",念了这个咒语:
async function 魔法函数() {
// 就可以在函数里使用"时间停止魔法":
// 普通代码:立即执行
console.log('第一步')
// await:时间停止,等这个完成
const 结果 = await 异步操作()
// 继续执行
console.log('第二步:', 结果)
}
// 总结成口诀:
// async 放函数前
// await 放 Promise 前
// 错误用 try-catch
// 并行用 Promise.all
10. 练习:你来试试
把下面的回调代码改成 async/await:
// 原代码(回调地狱)
function 做饭() {
煮饭(function(饭) {
炒菜(function(菜) {
摆桌(function() {
吃饭(饭, 菜)
})
})
})
}
// 改成 async/await(我帮你开个头):
async function 做饭() {
try {
const 饭 = await 煮饭()
// 你的代码...
} catch(错误) {
console.log('做饭失败:', 错误)
}
}
11. 常见问题解答
Q:什么时候用 async/await?
A:只要处理异步操作(请求数据、定时器、读取文件等)
Q:一定要用 try...catch 吗?
A:建议用,否则出错时程序会崩溃
Q:可以在普通函数里用 await 吗?
A:不行!await 必须在 async 函数里
Q:async 函数返回什么?
A:总是返回 Promise,即使你 return 一个普通值
async function 例子() {
return 'hello' // 自动包装成 Promise
}
例子().then(结果 => {
console.log(结果) // 'hello'
})
12. 实际应用场景
<template>
<div>
<button @click="加载数据">加载用户</button>
<div v-if="loading">加载中...</div>
<div v-else>{{ userData }}</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const loading = ref(false)
const userData = ref(null)
// async/await 让代码很清晰
async function 加载数据() {
try {
loading.value = true
// 看起来像同步的,很易读
const 用户 = await axios.get('/api/user/1')
const 订单 = await axios.get(`/api/orders/${用户.data.id}`)
const 地址 = await axios.get(`/api/address/${用户.data.id}`)
// 合并数据
userData.value = {
...用户.data,
订单: 订单.data,
地址: 地址.data
}
} catch (错误) {
console.error('加载失败', 错误)
alert('加载失败,请重试')
} finally {
loading.value = false // 无论成功失败,都要关闭loading
}
}
</script>
现在你理解了吗?我们来个小测试:
async function 测试() {
console.log('1')
await new Promise(resolve => {
setTimeout(() => {
console.log('2')
resolve()
}, 1000)
})
console.log('3')
}
测试()
console.log('4')
// 输出顺序是什么?为什么?
告诉我你的答案,然后我们继续深入学习!😊
1423