资讯动态

JavaScript / TypeScript 易踩坑知识点 —— 异步编程类

发布时间:2026/9/7 20:41:37 来源:尧图企业网站定制
JavaScript / TypeScript 易踩坑知识点 —— 异步编程类一、回调地狱Callback Hell问题说明多层异步回调嵌套会导致代码难以阅读和维护形成箭头型代码。踩坑示例// ❌ 回调地狱getData(function(a){getMoreData(a,function(b){getEvenMoreData(b,function(c){getFinalData(c,function(d){console.log(d)})})})})修复方案// ✅ 方案1Promise 链式调用getData().then(agetMoreData(a)).then(bgetEvenMoreData(b)).then(cgetFinalData(c)).then(dconsole.log(d)).catch(errconsole.error(err))// ✅ 方案2async/await推荐asyncfunctionfetchData(){try{constaawaitgetData()constbawaitgetMoreData(a)constcawaitgetEvenMoreData(b)constdawaitgetFinalData(c)console.log(d)}catch(err){console.error(err)}}二、Promise 错误处理缺失问题说明Promise 链中忘记添加.catch()会导致错误被静默吞掉难以排查。踩坑示例// ❌ 错误被静默吞掉fetchData().then(dataprocess(data)).then(resultsave(result))// 没有 .catch()任何错误都不会被捕获// ✅ 正确链式末尾加 .catch()fetchData().then(dataprocess(data)).then(resultsave(result)).catch(errconsole.error(处理失败:,err))// ✅ 更推荐async/await try/catchasyncfunctionmain(){try{constdataawaitfetchData()constresultawaitprocess(data)awaitsave(result)}catch(err){console.error(处理失败:,err)}}中间步骤遗漏 catch// ❌ 第一步的 catch 遗漏了fetchData().catch(errconsole.error(获取数据失败))// 只 catch 了第一步.then(dataprocess(data))// process 报错不会被捕获.catch(errconsole.error(处理失败))// ✅ 统一在末尾 catchfetchData().then(dataprocess(data)).then(resultsave(result)).catch(err{console.error(出错了:,err)})三、async/await 中忘记 try/catch问题说明async函数返回的是 Promise内部的错误如果没有被try/catch捕获会变为未处理的 Promise rejection。踩坑示例// ❌ 忘记 try/catchasyncfunctiongetUser(){constresawaitfetch(/api/user)// 网络错误会未捕获constdataawaitres.json()returndata}// 调用方如果没有 catch错误会静默失败getUser()// 错误未被处理// ✅ 正确内部或外部捕获// 方案1内部捕获asyncfunctiongetUser(){try{constresawaitfetch(/api/user)constdataawaitres.json()returndata}catch(err){console.error(获取用户失败:,err)throwerr// 重新抛出让调用方处理}}// 方案2外部捕获推荐asyncfunctionmain(){try{constuserawaitgetUser()console.log(user)}catch(err){console.error(请求失败:,err)}}四、Promise.all 的错误处理问题说明Promise.all中任何一个 Promise 失败整个 all 会立即 reject且其他请求会被中断。踩坑示例// ❌ 任何一个失败其他请求被中断const[users,posts,comments]awaitPromise.all([fetch(/api/users),fetch(/api/posts),fetch(/api/comments)])// ✅ 方案1用 Promise.allSettled 等待所有完成constresultsawaitPromise.allSettled([fetch(/api/users),fetch(/api/posts),fetch(/api/comments)])results.forEach((result,i){if(result.statusfulfilled){console.log(请求${i}成功:,result.value)}else{console.log(请求${i}失败:,result.reason)}})// ✅ 方案2给每个请求单独加 catchasyncfunctionsafeFetch(url){try{constresawaitfetch(url)returnawaitres.json()}catch(err){return{error:err.message}}}const[users,posts,comments]awaitPromise.all([safeFetch(/api/users),safeFetch(/api/posts),safeFetch(/api/comments)])五、async 函数中的 for 循环问题说明在循环中使用await时for...of和forEach的行为完全不同。踩坑示例consturls[/api/a,/api/b,/api/c]// ❌ forEach 中的 await 不会等待urls.forEach(async(url){constdataawaitfetch(url)console.log(data)})console.log(所有请求完成)// 这行会先执行// ✅ 用 for...of 按顺序执行for(consturlofurls){constdataawaitfetch(url)console.log(data)}// ✅ 用 Promise.all 并行执行不关心顺序时constresultsawaitPromise.all(urls.map(urlfetch(url)))六、setTimeout 与 async/await 的混淆问题说明setTimeout返回undefined不是 Promise不能直接await。踩坑示例// ❌ setTimeout 返回 undefinedawait 没有意义asyncfunctionwait(){awaitsetTimeout(()console.log(2秒后),2000)console.log(这行会立即执行)// 不会等 2 秒}// ✅ 正确将 setTimeout 包装为 Promisefunctionsleep(ms:number){returnnewPromise(resolvesetTimeout(resolve,ms))}asyncfunctionwait(){awaitsleep(2000)console.log(2秒后执行)// ✅ 正确等待}七、竞态条件Race Condition问题说明在快速输入如搜索框时多个异步请求可能以不确定顺序返回导致旧请求的结果覆盖新请求的结果。踩坑示例// ❌ 竞态条件用户快速输入 abc请求顺序 a-ab-abc// 但返回顺序可能是 abc-a-ab导致显示错误的结果constsearchasync(keyword:string){constresawaitfetch(/api/search?q${keyword})constdataawaitres.json()renderResults(data)// ⚠️ 可能是旧请求的结果}// ✅ 方案1用 AbortController 取消旧请求letcontroller:AbortController|nullnullconstsearchasync(keyword:string){controller?.abort()// 取消上一次的请求controllernewAbortController()try{constresawaitfetch(/api/search?q${keyword},{signal:controller.signal})constdataawaitres.json()renderResults(data)}catch(err){if(err.nameAbortError){console.log(请求已被取消)}}}// ✅ 方案2用序列号防覆盖letseq0constsearchasync(keyword:string){constcurrentSeqseqconstresawaitfetch(/api/search?q${keyword})constdataawaitres.json()if(currentSeq!seq)return// 有更新的请求丢弃旧结果renderResults(data)}八、Promise 构造函数反模式问题说明用new Promise包装已有的 Promise API 是反模式会破坏错误传播。踩坑示例// ❌ 反模式不必要的 Promise 包装functiongetUsers(){returnnewPromise((resolve,reject){fetch(/api/users).then(resres.json()).then(dataresolve(data)).catch(errreject(err))})}// ✅ 直接返回 PromisefunctiongetUsers(){returnfetch(/api/users).then(resres.json())}// ✅ async/await 写法asyncfunctiongetUsers(){constresawaitfetch(/api/users)returnawaitres.json()}总结对照表坑点错误写法正确写法回调地狱多层嵌套回调async/awaitPromise 错误吞掉不加 .catch()链式末尾加 .catch()async 未捕获不在 try/catch 中调用外部 try/catch 或内部捕获Promise.all 中断一个失败全部中断Promise.allSettledforEach awaitforEach 中用 awaitfor…ofsetTimeout awaitawait setTimeout()包装为 Promise竞态条件不控制请求顺序AbortController 或序列号Promise 反模式new Promise 包装已有 Promise直接返回 Promise

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价