关于JS中的"async/await"语法的用法与限制

简单来说,就是让异步代码看起来像同步。是 JavaScript 中基于 Promise 的处理异步操作的一种语法糖。

背景

回调地狱 (Callback Hell)

1
2
3
4
5
6
7
8
9
getData(function (a) {
getMoreData(a, function (b) {
getEvenMoreData(b, function (c) {
getFinalData(c, function (result) {
console.log(result);
});
});
});
});

全是缩进,可读性差。

后来 ES6 中出现 Promise 的链式调用

1
2
3
4
5
6
getData()
.then((a) => getMoreData(a))
.then((b) => getEvenMoreData(b))
.then((c) => getFinalData(c))
.then((result) => console.log(result))
.catch((err) => console.error(err));

但是尽管如此,还是有条件判断麻烦, try/catch 捕捉不到 Promise 内部错误等缺陷。

于是,像阻塞但是实际不阻塞的语法出现,同时可读性大大提升。

主要原理

async/await 的设计思想类很似 Generator + co,但并不是基于 Generator 实现的。
它是 V8 引擎原生支持的特性,性能更好,机制更直接。

也就是,await 把后续代码注册成 Promise 的 .then 回调,放入微任务队列。(交给 Promise 处理)

1
2
3
4
5
6
7
8
9
10
console.log("1");

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

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

await Promise.resolve()会把 console.log('3')放进微任务队列。
当前同步代码 console.log('4')执行完后,事件循环才处理微任务。
这就是 await 的真相:
它不是真的暂停,而是把后续逻辑放进微任务,等当前同步代码执行完再执行。

基本用法

1
2
3
4
5
6
7
8
9
async function fetchData() {
try {
const response = await fetch("/api/user");
const user = await response.json();
console.log(user);
} catch (error) {
console.error("出错了:", error);
}
}

async 返回的是 Promise。如果 return 一个值,会自动包装成 Promise.resolve(value)。如果 throw 错误,会变成 Promise.reject(error)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function hello() {
return "world";
}

hello(); // 返回的是 Promise<string>

// 也就是
hello().then(console.log); // 'world'

async function errorFunc() {
throw new Error("boom!");
}

errorFunc().catch((err) => console.log(err.message)); // 'boom!'

await 只能在 async 中使用,同时 await 等的是一个 Promise。如果不是 Promise,也会自动包装成 Promise.resolve()。

1
2
3
4
const result = await somePromise();

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

怎么用

多个接口全部返回

Promise.all 同时发起三个请求,谁也不等谁。等全部 resolve,再一起返回。Promise.all 是“全成功才成功”,任何一个 reject,整个就 reject。

如果希望“失败也不影响”,用 Promise.allSettled。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function bad() {
const a = await fetchA(); // 等 200ms
const b = await fetchB(); // 再等 200ms
const c = await fetchC(); // 再等 200ms
// 总耗时 ≈ 600ms
}

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

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

results.forEach((result) => {
if (result.status === "fulfilled") {
console.log(result.value);
} else {
console.log("失败:", result.reason);
}
});

条件判断 + 异步

用户登录后,先查权限,再决定加载哪个页面。(用 Promise 链写,嵌套 .then 里的 .then。)

1
2
3
4
5
6
7
8
9
10
11
async function loadPage() {
const user = await fetchUser();

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

循环 + await

1
2
3
4
5
6
7
8
9
10
11
12
async function Loop1() {
const ids = [1, 2, 3, 4, 5];
for (let id of ids) {
const user = await fetchUser(id); // 一个一个等,串行但顺序
process(user);
}
}

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

需要注意

忘记 try/catch

如果 fetch 失败,这个函数会抛出异常。但没接,就变成未捕获的 Promise rejection。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function forgotCatch() {
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 (err) {
console.error("请求失败:", err);
return null;
}
}

// 或者在外面 catch
safeFetch().catch((err) => console.log(err));

forEach/map 中 await 无效

map 的回调是 async 函数,返回的是 Promise。但 map 本身不会 await 这些 Promise。所以这些请求是并发的,但主流程不等它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const urls = ["/a", "/b", "/c"];

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

console.log("done"); // 这行会先打印!

for (let url of urls) {
const res = await fetch(url);
console.log(await res.text());
}
console.log("done"); // 这次等完了才打印

// 或者 Promise.all 包裹
await Promise.all(
urls.map(async (url) => {
const res = await fetch(url);
console.log(await res.text());
})
);

其他用法

异步迭代器 (处理数据流)

1
2
3
4
5
async function processStream(stream) {
for await (const chunk of stream) {
await processChunk(chunk);
}
}

自动重试

1
2
3
4
5
6
7
8
9
10
11
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 sleep(1000 * (i + 1)); // 重试间隔逐渐增加
}
}
}

超时控制

1
2
3
4
5
6
7
8
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]);
}

推荐阅读

关于JS中的"async/await"语法的用法与限制

https://blog.counpy.me/2025/11/async-await/

作者

Jhuoer Yen

发布于

2025-11-26

更新于

2025-11-26

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×