JavaScript 异步编程

张开发
2026/4/18 8:25:19 15 分钟阅读

分享文章

JavaScript 异步编程
JavaScript 异步编程学习笔记JavaScript 是单线程语言这意味着它同一时间只能执行一段代码。为了解决耗时操作如网络请求、文件读写、定时器阻塞主线程的问题JavaScript 发展出了强大的异步编程模型。1.核心概念概念说明同步 (Synchronous)代码按顺序一行行执行前一个任务完成前后一个任务无法开始。异步 (Asynchronous)耗时任务被挂起主线程继续执行后续代码任务完成后通过回调通知主线程。事件循环 (Event Loop)JavaScript 运行时浏览器/Node.js的核心机制负责将“已完成”的异步任务回调推入执行队列等待主线程空闲时执行。宏任务 (MacroTask)setTimeout,setInterval,I/O,UI 渲染。微任务 (MicroTask)Promise.then,MutationObserver,queueMicrotask。执行顺序规则同步代码 微任务 宏任务。2.异步编程的演进历程(1) 回调函数 (Callback) - 早期方案将函数作为参数传递给另一个函数在任务完成后执行。// 嵌套回调回调地狱fs.readFile(file1.txt,(err,data1){if(err)returnconsole.error(err);fs.readFile(file2.txt,(err,data2){if(err)returnconsole.error(err);fs.readFile(file3.txt,(err,data3){// ... 层层嵌套难以维护console.log(data1data2data3);});});});缺点代码嵌套深回调地狱错误处理困难逻辑不清晰。(2) Promise - ES6 标准将异步操作封装为一个对象代表“未来”的某个结果。状态pending(进行中)fulfilled(已成功)rejected(已失败)constfetchData(){returnnewPromise((resolve,reject){setTimeout((){constsuccesstrue;if(success){resolve(数据获取成功);}else{reject(数据获取失败);}},1000);});};// 链式调用fetchData().then(result{console.log(result);return下一步;}).then(next{console.log(next);}).catch(error{console.error(发生错误:,error);}).finally((){console.log(无论成功失败都会执行);});优点链式调用避免嵌套统一错误处理 (catch)。缺点状态一旦改变不可逆无法中途取消错误堆栈不清晰。(3) Async/Await - ES2017 (现代最佳实践)基于 Promise 的语法糖让异步代码看起来像同步代码。asyncfunctiongetData(){try{// 等待 Promise 完成constresultawaitfetchData();console.log(result);constnextawaitPromise.resolve(下一步);console.log(next);return完成;}catch(error){console.error(发生错误:,error);}finally{console.log(清理工作);}}getData();优点代码清晰易读错误处理符合同步习惯 (try...catch)调试方便。注意await只能在async函数内部使用。3.Event Loop (事件循环) 执行顺序理解执行顺序是掌握异步编程的关键。console.log(1. 同步开始);setTimeout((){console.log(4. 宏任务 (setTimeout));},0);Promise.resolve().then((){console.log(3. 微任务 (Promise));});console.log(2. 同步结束);// 输出顺序// 1. 同步开始// 2. 同步结束// 3. 微任务 (Promise)// 4. 宏任务 (setTimeout)执行流程执行所有同步代码。清空微任务队列Promise.then 等。执行一个宏任务setTimeout 等。执行 UI 渲染浏览器。回到步骤 2循环往复。4.常用异步 API(1)fetchAPI (网络请求)现代浏览器替代XMLHttpRequest的标准。asyncfunctiongetUserData(userId){try{constresponseawaitfetch(https://api.example.com/users/${userId});if(!response.ok){thrownewError(HTTP 错误状态码${response.status});}constdataawaitresponse.json();// 解析 JSONconsole.log(data);returndata;}catch(error){console.error(请求失败:,error);}}(2)Promise.all(并发处理)等待多个 Promise 全部成功或其中一个失败。constp1fetch(/api/user);constp2fetch(/api/posts);Promise.all([p1,p2]).then(([userRes,postsRes]){returnPromise.all([userRes.json(),postsRes.json()]);}).then(([user,posts]){console.log(全部加载完成,user,posts);}).catch(err{console.error(任意一个失败,err);});(3)Promise.race(竞速)只要有一个 Promise 完成无论成功失败立即返回。Promise.race([fetch(/api/slow),newPromise((_,reject)setTimeout(()reject(超时),5000))]).then(dataconsole.log(data)).catch(errconsole.error(err));// 5秒后触发超时(4)async/await并发优化使用Promise.all配合await实现并发。asyncfunctionfetchAll(){// 并发执行同时发起请求const[users,posts]awaitPromise.all([fetch(/api/users).then(rr.json()),fetch(/api/posts).then(rr.json())]);console.log(users,posts);}5.常见陷阱与最佳实践陷阱 1在循环中误用await// ❌ 串行执行慢for(consturlofurls){constresawaitfetch(url);// 必须等上一个完成constdataawaitres.json();console.log(data);}// ✅ 并发执行快constpromisesurls.map(urlfetch(url).then(rr.json()));constresultsawaitPromise.all(promises);陷阱 2忘记awaitasyncfunctiongetData(){constresfetch(/api/data);// 忘记 awaitconsole.log(res);// 输出 Promise 对象而不是数据}陷阱 3顶层await(Top-level await)在 ES 模块 (.mjs或script typemodule) 中可以直接在模块顶层使用await无需包裹在函数中。// module.jsconstdataawaitfetch(/api/data);exportconstjsonawaitdata.json();6.总结对比特性CallbackPromiseAsync/Await代码可读性差 (嵌套)中 (链式)优 (同步风格)错误处理分散在每个回调catch链try...catch调试难度难 (堆栈断裂)中易执行控制困难中等强推荐场景遗留代码简单链式调用复杂业务逻辑最佳实践建议优先使用async/await代码最清晰。处理多个独立请求时使用Promise.all实现并发。处理依赖请求时B 依赖 A 的结果使用串行await。始终使用try...catch捕获异步错误。避免在循环中串行await除非必须按顺序执行。异步编程是 JavaScript 的精髓掌握它对于开发高性能 Web 应用至关重要

更多文章