JS 入门通关手册(43):async/await 原理与异常处理(实战 + 面试,彻底搞懂)

张开发
2026/4/10 1:53:22 15 分钟阅读

分享文章

JS 入门通关手册(43):async/await 原理与异常处理(实战 + 面试,彻底搞懂)
摘要本文深入解析 async/await 的底层原理、语法规范、异常处理技巧结合大量真实业务场景拆解 async/await 与 Promise、Generator 的关联提供优雅的异常捕获方案对比 Promise 链式调用的优劣覆盖面试高频考点原理、异常处理、串行 / 并行实践帮助开发者摆脱回调嵌套与链式调用的繁琐写出更简洁、易维护的异步代码。一、前言async/await 为什么能替代 Promise 链式调用Promise 的出现解决了回调地狱但当异步操作较多时链式调用then 链依然会显得繁琐代码可读性、可维护性不足且异常处理需要层层 catch不够优雅。Promise 链式调用的痛点示例javascript运行// 串行请求获取用户 → 获取订单 → 获取详情 getRequest(/api/user) .then((user) getRequest(/api/order?userId${user.id})) .then((order) getRequest(/api/detail?orderId${order.id})) .then((detail) { console.log(detail); // 后续逻辑... }) .catch((err) { console.log(请求错误, err); });虽然比回调地狱简洁但依然需要不断嵌套 then逻辑连贯性差异常捕获只能统一处理无法精准捕获单个请求错误。async/await 的优势async/await 是Promise Generator 函数的语法糖它的核心作用是用同步代码的写法实现异步操作彻底摆脱 then 链代码更简洁、逻辑更清晰异常处理更灵活。改造后async/await 版本javascript运行// 同步写法实现异步逻辑 async function getDetail() { try { const user await getRequest(/api/user); const order await getRequest(/api/order?userId${user.id}); const detail await getRequest(/api/detail?orderId${order.id}); console.log(detail); } catch (err) { console.log(请求错误, err); } }代码完全是同步的结构却能执行异步操作可读性大幅提升异常处理也更灵活。二、async/await 基础语法必背1. 核心定义async用于修饰函数标记该函数是一个异步函数函数执行后永远返回一个 Promise无论函数内部返回什么值都会被转为Promise.resolve(值)。await只能用在 async 函数内部用于等待一个 Promise 完成fulfilled 或 rejected暂停函数执行直到 Promise 有结果后再继续执行后续代码。2. 基础用法3 个核心示例示例 1基本使用成功场景javascript运行// async 修饰函数返回 Promise async function fn() { // await 等待 Promise 成功获取结果 const res await Promise.resolve(异步操作成功); console.log(res); // 异步操作成功 return res; // 等同于 return Promise.resolve(res) } // 调用 async 函数 fn().then((res) console.log(函数返回结果, res)); // 函数返回结果异步操作成功示例 2await 等待失败的 Promisejavascript运行async function fn() { try { // await 等待 Promise 失败会抛出错误需用 try/catch 捕获 const res await Promise.reject(new Error(异步操作失败)); console.log(res); // 不会执行因为前面抛出错误 } catch (err) { console.log(捕获错误, err.message); // 捕获错误异步操作失败 } } fn();示例 3async 函数返回值自动转为 Promisejavascript运行// 1. 返回普通值 → 转为 Promise.resolve(普通值) async function fn1() { return 100; } fn1().then((res) console.log(res)); // 100 // 2. 返回 Promise → 直接返回该 Promise async function fn2() { return Promise.resolve(200); } fn2().then((res) console.log(res)); // 200 // 3. 抛出错误 → 转为 Promise.reject(错误) async function fn3() { throw new Error(主动抛出错误); } fn3().catch((err) console.log(err.message)); // 主动抛出错误3. 关键注意点面试避坑await 只能用在 async 函数内部普通函数中使用会报错语法错误。async 函数执行时遇到 await 会暂停执行不会阻塞整个 JS 线程只是暂停当前函数直到 Promise 完成。await 后面可以跟任意值如果是 Promise等待其结果如果是非 Promise直接返回该值等同于await Promise.resolve(值)。async 函数的返回值永远是 Promise即使没有 return 语句也会返回Promise.resolve(undefined)。三、async/await 底层原理面试必考核心结论async/await 本质是 Generator 函数 Promise 的语法糖浏览器 / Node.js 内部会自动执行 Generator 函数无需手动调用 next () 方法简化了 Generator 函数的使用。1. 先了解 Generator 函数基础铺垫Generator 函数是 ES6 推出的异步编程方案核心是 “可暂停、可恢复”用function*定义用yield暂停执行用next()恢复执行。示例Generator 实现异步javascript运行// Generator 函数带 * function* gen() { yield Promise.resolve(1); // 暂停返回 Promise yield Promise.resolve(2); // 暂停返回 Promise return 3; } // 手动执行 Generator 函数 const g gen(); g.next().value.then((res) { console.log(res); // 1 return g.next().value; }).then((res) { console.log(res); // 2 return g.next().value; }).then((res) { console.log(res); // 3 });可以看到Generator 函数需要手动调用 next ()且需要结合 then 链处理异步使用繁琐 —— 这就是 async/await 要解决的问题。2. async/await 与 Generator 的关联async 函数本质上是对 Generator 函数的 “自动执行封装”相当于用 async 替代function*用 await 替代yield自动执行 Generator 函数的 next () 方法无需手动调用自动处理 Promise 结果将结果返回给 await无需手动 then3. 手动模拟 async/await面试加分用 Promise 自动执行 Generator 函数模拟 async/await 的底层逻辑javascript运行// 模拟 async/await 的自动执行器 function myAsync(genFn) { // 返回一个 Promise对应 async 函数的返回值 return new Promise((resolve, reject) { const gen genFn(); // 执行 Generator 函数得到迭代器 // 自动执行 next() function next(res) { const result gen.next(res); // 执行 next获取 { value, done } if (result.done) { // Generator 执行完毕resolve 最终结果 return resolve(result.value); } // 若 value 是 Promise等待其完成后继续执行 next Promise.resolve(result.value).then( (val) next(val), // 成功将结果传给下一个 yield (err) reject(err) // 失败直接 reject ); } // 启动自动执行 next(); }); } // 测试模拟的 async/await function* gen() { const a yield Promise.resolve(1); const b yield Promise.resolve(a 1); return b 1; } // 用 myAsync 模拟 async/await 调用 myAsync(gen).then((res) console.log(res)); // 3通过这个模拟能清晰看到async/await 就是帮我们自动执行 Generator 函数自动处理 Promise 结果简化了手动调用的繁琐操作。面试必答async/await 与 Promise 的关系async/await 是基于 Promise 实现的不能脱离 Promise 单独使用await 后面必须是 Promise 或可转为 Promise 的值。async 函数的返回值是 Promise因此可以继续用 then/catch 处理结果。async/await 是 Promise 的 “语法糖”没有新增异步机制只是简化了 Promise 的使用方式。四、async/await 异常处理实战重点async/await 的异常处理核心是try/catch 语句比 Promise 的 catch 更灵活可以精准捕获单个异步操作的错误也可以统一捕获所有错误。1. 统一捕获所有错误简单场景用 try 包裹所有 await 操作catch 捕获所有异步错误包括单个 await 的失败javascript运行async function fetchData() { try { // 所有 await 操作都在 try 中 const user await getRequest(/api/user); const order await getRequest(/api/order?userId${user.id}); const detail await getRequest(/api/detail?orderId${order.id}); console.log(detail); } catch (err) { // 统一捕获所有错误任何一个 await 失败都会进入 catch console.log(捕获错误, err.message); // 可做错误处理如提示用户、重试请求 } }2. 精准捕获单个错误复杂场景如果需要对不同的异步操作做不同的错误处理可给每个 await 单独添加 try/catchjavascript运行async function fetchData() { let user null; // 捕获获取用户的错误 try { user await getRequest(/api/user); } catch (err) { console.log(获取用户失败, err.message); return; // 失败后终止后续操作 } let order null; // 捕获获取订单的错误 try { order await getRequest(/api/order?userId${user.id}); } catch (err) { console.log(获取订单失败, err.message); // 可做重试逻辑 order await getRequest(/api/order?userId${user.id}); } // 捕获获取详情的错误 try { const detail await getRequest(/api/detail?orderId${order.id}); console.log(detail); } catch (err) { console.log(获取详情失败, err.message); } }3. 结合 Promise.catch 处理错误补充方案await 后面的 Promise 可以直接调用 catch捕获单个异步操作的错误不影响后续代码执行javascript运行async function fetchData() { // 单个 await 错误捕获不影响后续 await 执行 const user await getRequest(/api/user).catch((err) { console.log(获取用户失败, err.message); return null; // 失败后返回默认值避免后续代码报错 }); if (!user) return; // 若获取用户失败终止后续操作 const order await getRequest(/api/order?userId${user.id}).catch((err) { console.log(获取订单失败, err.message); return null; }); if (!order) return; const detail await getRequest(/api/detail?orderId${order.id}); console.log(detail); }4. 面试避坑常见异常处理误区误区 1忘记用 try/catchawait 失败会导致整个 async 函数返回 rejected Promise且错误会 “冒泡”若未捕获会触发 Uncaught Error。误区 2多个 await 并行执行时用 try/catch 统一捕获无法区分哪个 await 失败需单独捕获或结合 Promise.all 处理。误区 3认为 await 会阻塞整个 JS 线程 —— 其实不会await 只暂停当前 async 函数JS 线程会继续执行其他同步代码。五、async/await 实战场景真实项目常用场景 1异步串行执行按顺序执行多个异步操作最常用场景多个异步操作有依赖关系如先获取用户再根据用户 ID 获取订单javascript运行// 串行请求用户 → 订单 → 详情 async function getOrderDetail() { try { // 1. 获取用户 const { data: user } await axios.get(/api/user); // 2. 根据用户ID获取订单依赖用户数据 const { data: order } await axios.get(/api/order?userId${user.id}); // 3. 根据订单ID获取详情依赖订单数据 const { data: detail } await axios.get(/api/detail?orderId${order.id}); // 渲染数据 renderDetail(detail); return detail; } catch (err) { console.log(请求失败, err); ElMessage.error(获取数据失败请重试); // 结合UI组件提示 } }场景 2异步并行执行同时执行多个异步操作提高效率场景多个异步操作无依赖关系如同时获取首页多个模块的数据用 Promise.all 结合 async/await 实现并行javascript运行// 并行请求同时获取首页轮播、推荐、公告数据 async function getHomeData() { try { // 用 Promise.all 并行执行多个请求 const [bannerRes, recommendRes, noticeRes] await Promise.all([ axios.get(/api/banner), axios.get(/api/recommend), axios.get(/api/notice), ]); // 分别处理数据 const banner bannerRes.data; const recommend recommendRes.data; const notice noticeRes.data; // 渲染首页 renderHome(banner, recommend, notice); } catch (err) { console.log(首页数据请求失败, err); } }⚠️ 注意并行执行时只要有一个请求失败Promise.all 会立即失败进入 catch若需允许部分失败可用 Promise.allSettled。场景 3异步并行 部分容错允许部分请求失败javascript运行async function getHomeData() { // 用 allSettled 并行执行获取所有结果 const results await Promise.allSettled([ axios.get(/api/banner), axios.get(/api/recommend), axios.get(/api/notice), ]); // 筛选成功的结果处理失败的结果 const banner results[0].status fulfilled ? results[0].value.data : []; const recommend results[1].status fulfilled ? results[1].value.data : []; const notice results[2].status fulfilled ? results[2].value.data : []; // 即使部分失败也渲染首页用默认值替代失败数据 renderHome(banner, recommend, notice); // 记录失败的请求 const failRequests results.filter(item item.status rejected); if (failRequests.length 0) { console.log(部分请求失败, failRequests); } }场景 4异步重试机制失败后自动重试javascript运行// 封装带重试的请求函数 async function requestWithRetry(url, retryCount 3, delay 1000) { try { const { data } await axios.get(url); return data; } catch (err) { // 重试次数大于0继续重试 if (retryCount 0) { console.log(请求失败剩余重试次数${retryCount}${delay}ms后重试); // 延迟重试用 setTimeout 包裹返回 Promise await new Promise(resolve setTimeout(resolve, delay)); return requestWithRetry(url, retryCount - 1, delay); // 递归重试 } // 重试次数耗尽抛出错误 throw new Error(请求失败已重试${3}次${err.message}); } } // 使用 async function fetchData() { try { const data await requestWithRetry(/api/data, 3, 1000); console.log(data); } catch (err) { console.log(err.message); } }六、async/await vs Promise 链式调用面试必比表格对比项async/awaitPromise 链式调用代码结构同步写法逻辑连贯链式嵌套可读性一般异常处理try/catch 灵活捕获单个 / 统一只能链式 catch精准捕获繁琐调试体验可直接断点调试步骤清晰断点调试需跟进 then 链不够直观学习成本低同步思维易理解中需理解链式调用和状态流转适用场景复杂异步逻辑、多依赖异步操作简单异步操作、少量链式调用总结建议简单异步操作如单个接口请求用 Promise 或 async/await 均可看个人习惯。复杂异步逻辑多依赖、多步骤、需精准异常处理优先用 async/await代码更简洁、易维护、易调试。并行异步操作结合 Promise.all/allSettled async/await兼顾效率和可读性。七、高频面试题必背标准答案async/await 是什么底层原理是什么答async/await 是 Promise Generator 函数的语法糖用于用同步代码的写法实现异步操作底层是通过自动执行 Generator 函数结合 Promise 处理异步结果简化了 Generator 函数的手动调用。async 函数的返回值是什么答async 函数的返回值永远是一个 Promise如果函数内部返回普通值会转为 Promise.resolve (普通值)如果抛出错误会转为 Promise.reject (错误)。await 只能用在什么地方如果 await 后面跟的不是 Promise会怎么样答await 只能用在 async 函数内部如果 await 后面跟的不是 Promise会直接返回该值等同于 await Promise.resolve (值)。async/await 如何处理异常有几种方式答主要用 try/catch 处理异常有两种方式① 统一捕获try 包裹所有 awaitcatch 统一处理② 精准捕获每个 await 单独 try/catch或结合 Promise.catch。async/await 和 Promise 链式调用的区别各自的优缺点答async/await 是同步写法逻辑连贯、异常处理灵活、调试方便但依赖 async 函数Promise 链式调用是异步写法可读性一般、异常捕获繁琐但无需依赖 async 函数兼容性更好低版本浏览器需兼容。async/await 会阻塞 JS 线程吗为什么答不会await 只会暂停当前 async 函数的执行不会阻塞整个 JS 线程JS 线程会继续执行其他同步代码直到 await 等待的 Promise 完成再恢复当前 async 函数的执行。八、总结async/await 是 Promise Generator 的语法糖核心是 “同步写法实现异步操作”彻底简化异步代码。async 修饰函数返回 Promiseawait 只能用在 async 内部等待 Promise 完成并返回结果。异常处理核心是 try/catch可实现统一捕获或精准捕获比 Promise 链式调用更灵活。实战中串行异步用 await 顺序执行并行异步用 Promise.all await提高效率。掌握其底层原理Generator Promise和异常处理技巧是前端面试的必备能力。

更多文章