async/await 不是什么
很多工程师把 async/await 当成 JavaScript 独有的异步语法,甚至认为它和 Promise 是两套并行的机制。这是一个常见的误解。
async/await 是 Promise 的语法糖,不是独立于 Promise 的新机制。
async函数返回一个 Promiseawait等待一个 Promise resolved(或 rejected),然后以那个值(或错误)继续执行
理解这一点,是掌握 async/await 的前提。
async 函数的本质
async 函数的返回值
任何 async 函数的返回值,都会被 JavaScript 引擎自动包装成 Promise:
async function foo() {
return 42;
}
// 等价于:
function foo() {
return Promise.resolve(42);
}
如果 async 函数抛出异常,返回的 Promise 会被 rejected:
async function foo() {
throw new Error('oops');
}
// 等价于:
function foo() {
return Promise.reject(new Error('oops'));
}
async 函数的并行执行
async 函数是立即执行的,只是返回值是 Promise:
async function fetchUser() {
console.log('fetchUser start');
const response = await fetch('/api/user');
return response.json();
}
async function fetchPosts() {
console.log('fetchPosts start');
const response = await fetch('/api/posts');
return response.json();
}
// 两个函数会并发执行
const userPromise = fetchUser();
const postsPromise = fetchPosts();
// 等待两个都完成
const [user, posts] = await Promise.all([userPromise, postsPromise]);
await 的暂停机制
这是理解 async/await 执行模型的核心。
await 不是"阻塞等待",而是暂停当前函数的执行,将后续代码注册为微任务。
async function foo() {
console.log('1');
await Promise.resolve();
console.log('2');
}
console.log('3');
foo();
console.log('4');
执行过程:
console.log('3')同步输出- 调用
foo(),console.log('1')输出,await Promise.resolve()暂停foo,将console.log('2')注册为微任务 console.log('4')同步输出- 微任务执行,
console.log('2')输出
所以输出是:3, 1, 4, 2
await 之后的代码是微任务
await 后面的代码,不是"等 await 完成后立即执行",而是"作为微任务加入队列"。
async function foo() {
console.log('a');
await bar();
console.log('b');
}
function bar() {
return Promise.resolve();
}
await bar() 做了什么:
- 调用
bar(),返回 Promise - 将
console.log('b')作为微任务加入队列 - 暂停
foo的执行,控制权返回给调用者
多个 await 的执行顺序
async function foo() {
console.log('1');
await Promise.resolve();
console.log('2');
await Promise.resolve();
console.log('3');
await Promise.resolve();
console.log('4');
}
foo().then(() => console.log('done'));
// 输出: 1, 2, 3, 4, done
每个 await 都会产生一个微任务,它们按顺序执行。
async/await 与错误处理
try/catch
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('fetch failed:', error);
throw error;
}
}
catch 与 Promise.all
// 错误:Promise.all 不会等待 catch
const [a, b] = await Promise.all([
fetchA().catch(e => null),
fetchB().catch(e => null)
]);
常见陷阱
陷阱一:串行 instead of 并行
// 串行:慢
async function wrong() {
const a = await fetchA();
const b = await fetchB();
return [a, b];
}
// 并行:快
async function right() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return [a, b];
}
陷阱二:循环中的 await
// 串行执行
async function wrong(urls) {
const results = [];
for (const url of urls) {
const result = await fetch(url);
results.push(result);
}
return results;
}
// 并行执行
async function right(urls) {
const promises = urls.map(url => fetch(url));
return Promise.all(promises);
}
这一章想说的
async/await 是 Promise 的语法糖。理解这一点,就不会写出"在 async 函数里用 .then() 是多余的"这种错误认知。
await 的本质是暂停当前函数的执行,将后续代码注册为微任务。这是 JavaScript 单线程 + 事件循环模型的一部分,不是"阻塞等待"。
写出高效异步代码的关键是:需要并行的时候,不要串行。学会用 Promise.all 批量发起异步操作。
延展阅读
实践练习
练习:优化下列代码
async function getUserData(userId) {
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
const posts = await fetch(`/api/users/${userId}/posts`).then(r => r.json());
const comments = await fetch(`/api/users/${userId}/comments`).then(r => r.json());
return { user, posts, comments };
}
这段代码的性能问题是什么?如何优化?