说说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函数一定会返回一个 Promisejavascript体验AI代码助手代码解读复制代码async function hello() { return world; } hello(); // 返回的是 Promisestring所以你可以这么接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等的是一个 Promisejavascript体验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 3await 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); // 一个一个等串行 } }如果每个请求 100ms5 个就是 500ms。改法 1并行发起等全部完成javascript体验AI代码助手代码解读复制代码async function goodLoop1() { const ids [1, 2, 3, 4, 5]; await Promise.all(ids.map(id fetchUser(id))); }改法 2需要顺序处理用for...ofjavascript体验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...ofawait。五、async/await的坑你踩过几个坑 1忘记 try/catchjavascript体验AI代码助手代码解读复制代码async function forgotCatch() { const res await fetch(/api/data); // 如果网络出错 return res.json(); }如果fetch失败这个函数会抛出异常。但没人接就变成未捕获的 Promise rejection。浏览器Uncaught (in promise) TypeError: Failed to fetch解决加 try/catchjavascript体验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...ofjavascript体验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来源稀土掘金著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。