西藏自治区网站建设_网站建设公司_JavaScript_seo优化
2026/1/16 18:37:36 网站建设 项目流程

一、前言

async/await是什么?为什么会有它?

我们先说结论:

async/await 是 JavaScript 中处理异步操作的一种语法糖。
它基于Promise,但让异步代码写起来更像同步,读起来更清晰。

那问题来了:
为啥需要它?Promise 不香吗?

1. 为什么会有 async/await?

因为——回调地狱(Callback Hell)太难受了。

早年写异步,都是回调:

getData(function(a) {getMoreData(a, function(b) {getEvenMoreData(b, function(c) {getFinalData(c, function(result) {console.log(result);});});});
});

层层嵌套,缩进比代码还长。
一眼望去,全是花括号和 function

后来ES6推出了 Promise,链式调用,清爽多了:

getData().then(a => getMoreData(a)).then(b => getEvenMoreData(b)).then(c => getFinalData(c)).then(result => console.log(result)).catch(err => console.error(err));

看着是好了。
但实际开发中,你会发现:

  • 条件判断麻烦(比如:只有满足某个条件才继续下一步)
  • 中间变量传递绕(得靠then一层层传)
  • try/catch捕获不了 Promise 内部错误(得用 .catch()
  • 循环处理异步?写起来非常绕

于是,async/await 出现了。
它让我们可以用同步的写法,写异步逻辑。
看起来像“阻塞”,实则不阻塞。
关键是:可读性可以大大的提升。

二、async/await 怎么用?

1. 基本语法

async function fetchData() {try {const response = await fetch('/api/user');const user = await response.json();console.log(user);} catch (error) {console.error('出错了:', error);}
}

对,就这么简单,但你得明白三点:

1. async 函数一定会返回一个 Promise

async function hello() {return 'world';
}hello(); // 返回的是 Promise<string>

所以你可以这么接:

hello().then(console.log); // 'world'

补充:

  • 如果async 函数return一个值,会自动包装成Promise.resolve(value)
  • 如果throw错误,会变成 Promise.reject(error)
async function errorFunc() {throw new Error('boom!');
}errorFunc().catch(err => console.log(err.message)); // 'boom!'

2. await只能在async函数里用

function bad() {await fetchData(); // SyntaxError
}

await必须在async函数内部。否则,直接报错。

3. await 等的是一个 Promise

const result = await somePromise();

如果等的不是 Promise?
那也没事,JavaScript会自动把它包装成Promise.resolve()

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

三、async/await 到底做了啥?

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

你可以把它理解成: await把后续代码注册成Promise的.then回调,放入微任务队列。

await promise 等价于:
把 await 后面的代码,用 .then 包起来,交给 Promise 处理。

举个例子:

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

输出结果:

1
2
4
3

await Promise.resolve()会把console.log('3')放进微任务队列。
当前同步代码console.log('4')执行完后,事件循环才处理微任务。

这就是 await 的真相:
它不是真的暂停,而是把后续逻辑放进微任务,等当前同步代码执行完再执行。

四、async/await 怎么用才对?

光会写 await 不够,关键是怎么用得好。

场景 1:串行 vs 并行

你有三个接口,要等全部返回。

错误写法:串行等待

async function bad() {const a = await fetchA(); // 等 200msconst b = await fetchB(); // 再等 200msconst c = await fetchC(); // 再等 200ms// 总耗时 ≈ 600ms
}

一个接一个,慢得像蜗牛。

正确写法:并行发起

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

Promise.all同时发起三个请求,谁也不等谁。
等全部 resolve,再一起返回。

⚠️ 注意:Promise.all是“全成功才成功”,任何一个 reject,整个就reject。

如果你希望“失败也不影响”,用Promise.allSettled
const results = await Promise.allSettled([fetchA(),fetchB(),fetchC()
]);results.forEach((result) => {if (result.status === 'fulfilled') {console.log(result.value);} else {console.log('失败:', result.reason);}
});

场景 2:条件判断 + 异步

比如:用户登录后,先查权限,再决定加载哪个页面。

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

这种逻辑,用Promise链写,得嵌套 .then 里的 .then
async/await,会非常的清晰。

场景 3:循环中使用await

常见错误:

async function badLoop() {const ids = [1, 2, 3, 4, 5];for (let id of ids) {await fetchUser(id); // 一个一个等,串行!}
}

如果每个请求 100ms,5 个就是 500ms。

改法 1:并行发起,等全部完成

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

改法 2:需要顺序处理?用for...of

async function goodLoop2() {const ids = [1, 2, 3, 4, 5];for (let id of ids) {// 必须等上一个完成再进行下一个const user = await fetchUser(id);process(user);}
}

关键看需求:

要快?用Promise.all

要顺序?用for...of+await

五、async/await的坑,你踩过几个?

坑 1:忘记 try/catch

async function forgotCatch() {const res = await fetch('/api/data'); // 如果网络出错?return res.json();
}

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

浏览器:Uncaught (in promise) TypeError: Failed to fetch

解决:加 try/catch

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

坑 2:在 forEach/map 中用 await,无效!

const urls = ['/a', '/b', '/c'];urls.map(async (url) => {const res = await fetch(url);console.log(await res.text());
});console.log('done'); // 这行会先打印!

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

解决:用 for...of

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. 异步迭代器

处理数据流时很有用:

async function processStream(stream) {for await (const chunk of stream) {await processChunk(chunk);}
}

2. 重试机制

实现自动重试:

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)); // 重试间隔逐渐增加}}
}

3. 超时控制

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]);
}

七、总结

async/await只是让异步代码更好写、更好读。但它还是解决不了异步本身的复杂性。

所以,下次面试官问你:“讲讲 async/await”

你可以说:

  1. “它是 Promise 的语法糖,让异步代码更易读。
  2. 但它本质还是异步,await会把后续逻辑注册为 .then 回调,进入微任务队列。
  3. 使用时要注意并行优化、错误捕获,避免在数组方法中误用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询