当前位置: 首页 > news >正文

说说async/await?我差点翻车!原来还可以这么用

一、前言

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

我们先说结论:

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

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

1. 为什么会有 async/await?

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

早年写异步,都是回调:

javascript

体验AI代码助手

代码解读

复制代码

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

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

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

javascript

体验AI代码助手

代码解读

复制代码

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. 基本语法

javascript

体验AI代码助手

代码解读

复制代码

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

javascript

体验AI代码助手

代码解读

复制代码

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

所以你可以这么接:

javascript

体验AI代码助手

代码解读

复制代码

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

补充:

  • 如果async函数return一个值,会自动包装成Promise.resolve(value)
  • 如果throw错误,会变成Promise.reject(error)

javascript

体验AI代码助手

代码解读

复制代码

async function errorFunc() { throw new Error('boom!'); } errorFunc().catch(err => console.log(err.message)); // 'boom!'

2.await只能在async函数里用

javascript

体验AI代码助手

代码解读

复制代码

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

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

3.await等的是一个 Promise

javascript

体验AI代码助手

代码解读

复制代码

const result = await somePromise();

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

javascript

体验AI代码助手

代码解读

复制代码

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 处理。

举个例子:

javascript

体验AI代码助手

代码解读

复制代码

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

输出结果:

体验AI代码助手

代码解读

复制代码

1 2 4 3

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

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


四、async/await 怎么用才对?

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

场景 1:串行 vs 并行

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

错误写法:串行等待

javascript

体验AI代码助手

代码解读

复制代码

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

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

正确写法:并行发起

javascript

体验AI代码助手

代码解读

复制代码

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

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

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

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

javascript

体验AI代码助手

代码解读

复制代码

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:条件判断 + 异步

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

javascript

体验AI代码助手

代码解读

复制代码

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

常见错误:

javascript

体验AI代码助手

代码解读

复制代码

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

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

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

javascript

体验AI代码助手

代码解读

复制代码

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

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

javascript

体验AI代码助手

代码解读

复制代码

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

javascript

体验AI代码助手

代码解读

复制代码

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

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

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

解决:加 try/catch

javascript

体验AI代码助手

代码解读

复制代码

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()

javascript

体验AI代码助手

代码解读

复制代码

safeFetch().catch(err => console.log(err));

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

javascript

体验AI代码助手

代码解读

复制代码

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

javascript

体验AI代码助手

代码解读

复制代码

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

或者用Promise.all包一层:

javascript

体验AI代码助手

代码解读

复制代码

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

六、高级用法和技巧

1. 异步迭代器

处理数据流时很有用:

js

体验AI代码助手

代码解读

复制代码

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

2. 重试机制

实现自动重试:

js

体验AI代码助手

代码解读

复制代码

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. 超时控制

js

体验AI代码助手

代码解读

复制代码

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只是让异步代码更好写、更好读。但它还是解决不了异步本身的复杂性。

作者:程序员大华
链接:https://juejin.cn/post/7563547711006408738
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

http://www.jsqmd.com/news/566969/

相关文章:

  • Docker + 宝塔:容器化部署最佳实践(2026最新版)
  • Windows资源管理器美化终极指南:如何免费添加毛玻璃效果
  • springboot+vue基于web的社区维修平台
  • 告别手动比对!用OrthoFinder 2.5.4一键搞定多物种同源基因分析(附保姆级配置流程)
  • Avalonia中ComboBox数据绑定的3种实战用法(附完整代码示例)
  • Vision Transformer——打破CNN垄断的视觉革命先锋
  • 千问3.5-2B镜像实战:免conda/pip安装,网页端直接调用内置视觉语言模型
  • STL容器与算法:C++高效编程的5个实用技巧
  • 从CVPR 2025看Mamba:这个线性复杂度的‘新星’在视觉任务里到底行不行?
  • 2026年市面上知名的光固化保护套厂家怎么选择,光固化保护套/无溶剂环氧涂料/环氧酚醛,光固化保护套批发厂家有哪些 - 品牌推荐师
  • 告别手动复制!用VBA批量提取1000份PDF到TXT的隐藏技巧(含Acrobat版本适配指南)
  • Laravel-Vue SPA测试策略:单元测试与功能测试全覆盖
  • M0 事件 Event 机制
  • 解放Nordic芯片的复位引脚:一个被忽略的GPIO资源,附NCS/Zephyr配置指南
  • 告别虚拟机!在Windows 11上本地搭建GB28181模拟环境(含Wireshark抓包配置)
  • MIT研究人员使用人工智能发现材料中的原子缺陷
  • 微信小程序10MB存储不够用?手把手教你实现LRU缓存淘汰策略
  • Antv L7 + Mapbox 打造3D城市建筑可视化:从基础到进阶
  • 构建智能体的专业技能树 - 搞懂 Agent Skills(上篇)
  • 从零实现线性回归:深入解析PyTorch核心训练流程与梯度下降原理
  • springboot+vue基于web的社区蔬菜商城售卖网站的设计系统
  • Z-Image-Turbo-rinaiqiao-huiyewunv效果对比:启用显存卸载前后单图生成耗时与OOM概率
  • 20244118 2025-2026-2 《Python程序设计》实验1报告
  • GNSS形变监测系统 GNSS监测站
  • Markdown可视化进阶:用Markmap打造交互式思维导图的全攻略
  • AI辅助开发:打造会分析日志、懂推理的kernel32.dll修复智能助手
  • 亚马逊因伊朗无人机袭击免收整月AWS费用
  • 基于Dify平台快速构建MogFace-large人脸检测AI应用
  • 设计模式之抽象工厂模式
  • LFM2.5-1.2B-Thinking-GGUF实战:低资源环境下的高效文本生成体验