什么是 async/await?
async/await是 JavaScript 中处理异步操作的重要语法糖,用于简化异步操作的编写,使其看起来像同步代码。
基本用法
async/await 是基于 Promise 的,用于处理异步操作。它的基本用法如下:
// 定义一个异步函数
async function fetchData() {
try {
// 等待异步操作完成
const data = await someAsyncOperation();
// 处理异步操作的结果
return data;
} catch (error) {
// 处理错误
throw error;
}
}2
3
4
5
6
7
8
9
10
11
12
async 关键字详解
async 关键字用于定义一个异步函数,它必定会返回一个 Promise 对象:
- 无返回值:自动返回
Promise.resolve(undefined) - 有返回值:自动包装为
Promise.resolve(返回值) - 抛出错误:自动包装为
Promise.reject(错误)
// 示例:async 函数的返回值
async function noReturn() {
// 相当于 return Promise.resolve(undefined)
}
async function withReturn() {
return 'hello'; // 相当于 return Promise.resolve("hello")
}
async function withError() {
throw new Error('出错了'); // 相当于 return Promise.reject(new Error("出错了"))
}2
3
4
5
6
7
8
9
10
11
12
await 关键字详解
await 关键字只能在 async 函数内部使用,用于暂停函数执行直到 Promise 处理完成:
- 使用限制:只能在
async函数内使用,否则会报错 - 适用对象:Promise 对象或返回 Promise 的函数调用
- 自动包装:非 Promise 值会被自动包装为 resolved 状态的 Promise
- 错误处理:rejected Promise 会抛出错误,需用
try/catch捕获
// 示例:await 的使用
async function example() {
// 等待 Promise 解决
const result1 = await Promise.resolve('成功');
// 等待异步函数
const result2 = await someAsyncFunction();
// 非 Promise 值会被自动包装
const result3 = await '直接值'; // 相当于 await Promise.resolve("直接值")
// 错误处理
try {
const result4 = await Promise.reject('失败');
} catch (error) {
console.log('捕获错误:', error);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
解决了什么问题?
1. 解决 Promise 链式调用的复杂性
在 async/await 出现之前,处理多个异步操作通常需要使用 Promise 的链式调用,这会导致代码难以阅读和维护。
// 使用 Promise 链式调用
function getUserData() {
return fetchUser()
.then((user) => {
return fetchUserPosts(user.id);
})
.then((posts) => {
return fetchPostComments(posts[0].id);
})
.then((comments) => {
console.log('评论:', comments);
return comments;
})
.catch((error) => {
console.error('获取数据失败:', error);
});
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. 提供更直观的异步代码写法
async/await 让我们可以用同步的方式编写异步代码,大大提高了代码的可读性。
// 使用 async/await 处理异步操作
async function getUserData() {
try {
const user = await fetchUser();
const posts = await fetchUserPosts(user.id);
const comments = await fetchPostComments(posts[0].id);
console.log('评论:', comments);
return comments;
} catch (error) {
console.error('获取数据失败:', error);
}
}2
3
4
5
6
7
8
9
10
11
12
3. 简化错误处理
Promise 的错误处理需要通过 .catch() 方法,而 async/await 可以使用标准的 try/catch 语句。
// Promise错误处理
fetchData()
.then((data) => processData(data))
.then((result) => updateUI(result))
.catch((error) => {
// 难以确定错误发生在哪个阶段
handleError(error);
});
// async/await错误处理
async function handleData() {
try {
const data = await fetchData();
const result = await processData(data);
updateUI(result);
} catch (error) {
// 可以精确定位错误发生的位置
handleError(error);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
4. 更好的条件逻辑处理
async/await 可以让我们使用标准的 if/else 语句来处理异步条件判断,而不是使用 Promise 的 then() 方法。
// Promise条件逻辑
function checkUserStatus(userId) {
return fetchUser(userId)
.then((user) => {
if (user.isActive) {
return fetchUserData(user.id);
} else {
return fetchInactiveUserData(user.id);
}
})
.then((data) => {
if (data.hasPermission) {
return fetchSensitiveData(data.id);
} else {
return fetchPublicData(data.id);
}
});
}
// async/await条件逻辑
async function checkUserStatus(userId) {
const user = await fetchUser(userId);
const data = user.isActive ? await fetchUserData(user.id) : await fetchInactiveUserData(user.id);
return data.hasPermission ? await fetchSensitiveData(data.id) : await fetchPublicData(data.id);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
5. 优化循环处理
当需要在循环中处理异步操作时,async/await 可以让我们使用标准的 for 循环,而不是使用 Promise 的循环方法。
// Promise在循环中的使用
function processItems(items) {
return Promise.all(
items.map((item) => {
return fetchItem(item.id)
.then((data) => processData(data))
.then((result) => saveResult(result));
})
);
}
// async/await在循环中的使用
async function processItems(items) {
const results = [];
for (const item of items) {
const data = await fetchItem(item.id);
const result = await processData(data);
await saveResult(result);
results.push(result);
}
return results;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
6. 增强调试体验
async/await 可以让我们像同步代码一样调试异步代码,这在 Promise 链式调用中是非常困难的。
// 调试 Promise 链式调用
fetchData()
.then((data) => processData(data)) // 在这里设置断点不直观
.then((result) => updateUI(result));
// 调试 async/await 代码
async function handleData() {
const data = await fetchData(); // 可以在这里设置断点
const result = await processData(data); // 可以在这里设置断点
updateUI(result);
}2
3
4
5
6
7
8
9
10
11
设计原理
async/await 与 Generator 的关系
async/await 的设计思想与 Generator + co 库非常相似,但实现方式有本质区别:
- 相似之处:两者都能暂停函数执行,实现异步代码的同步化写法
- 本质区别:
async/await并非基于Generator实现,而是 V8 引擎原生支持的特性
技术细节:虽然早期
async/await的提案确实考虑过基于Generator实现,但最终 V8 团队选择了原生实现,避免了Generator的额外开销和复杂性。
底层实现机制
async/await 作为 V8 引擎原生特性,具有以下优势:
- 性能更优:无需额外的库转换,直接由引擎优化执行,避免了
Generator + co的状态机开销 - 机制更直接:
async函数被编译为状态机,await表达式直接转换为Promise链式调用,无需额外的库来管理执行流程 - 内存占用更低:不需要维护
Generator的迭代器对象和额外的执行上下文
工作原理简述
可以将 async/await 的工作原理简单理解为 暂停-注册-排队-恢复 的四步机制:
- 暂停执行:遇到
await时,函数执行暂停,等待 Promise 解决 - 注册回调:
await后续代码被自动注册为 Promise 的.then()回调 - 放入队列:回调函数被放入微任务队列等待执行
- 恢复执行:Promise 解决后,从微任务队列中取出回调继续执行
这种机制本质上是将同步风格的代码转换为底层的 Promise 链式调用,使开发者能够以直观的方式编写异步代码,同时保持了非阻塞的特性。它不是真的暂停,而是把后续逻辑放进微任务,等当前同步代码执行完再执行。
总结
- async/await 是基于 Promise 的语法糖,它使得异步代码看起来更像同步代码,提高了可读性和可维护性
- async 函数返回 Promise,await 表达式可以暂停函数执行,等待 Promise 解决
- await 只能在 async 函数中使用,否则会报错
- async/await 可以与 Promise 方法结合使用,实现更复杂的异步流程
TIP
如何判断一个函数是 async 标记函数
// 使用 constructor.name 判断
// 原理:AsyncFunction 构造函数的 name 属性为 'AsyncFunction'
function isAsyncFunction(func) {
return func.constructor.name === 'AsyncFunction';
}
// 或者 使用 Object.prototype.toString 判断
// 原理:AsyncFunction 构造函数的 toString 方法返回 '[object AsyncFunction]'
function isAsyncFunction(func) {
return Object.prototype.toString.call(func) === '[object AsyncFunction]';
}
console.log(isAsyncFunction(async function () {})); // true
console.log(isAsyncFunction(function () {})); // false2
3
4
5
6
7
8
9
10
11
12
13

