零基础鸿蒙应用开发第三十一节:await简化异步编程与Promise进阶并发

张开发
2026/4/12 4:50:18 15 分钟阅读

分享文章

零基础鸿蒙应用开发第三十一节:await简化异步编程与Promise进阶并发
【学习目标】掌握async/await语法糖的核心用法将 Promise 异步代码“同步化”书写彻底摆脱链式调用冗余熟练使用try/catch/finally处理异步错误覆盖同步异步场景的统一错误捕获理解Promise.allSettled/Promise.any的适用场景解决“批量任务全量结果统计”“多源数据容错获取”问题识别并解决await滥用导致的“并发变串行”问题优化异步任务执行性能结合鸿蒙商品数据请求场景落地 Async/await Promise 进阶方案。【学习重点】async/await与 Promise 的底层关联async函数返回 Promise、await等待 Promise 状态变更try/catch捕获await异步错误的核心逻辑以及finally的收尾作用Promise.allSettled/any与all/race的场景对比及代码实操await滥用的坑并发变串行及优化技巧先创建 Promise 实例再批量 await。一、工程准备本节基于上一节的PromiseAsyncBasicDemo工程扩展复制一份重命PromiseAsyncBasicDemo_1工程目录如下PromiseAsyncBasicDemo_1/ └── ets/ ├── pages/ │ └── Index.ets // 新增2个按钮Async/await、Promise进阶并发 └── utils/ // 新增asyncawaitDemo、promiseAdvancedDemo静态方法 └── PromiseUtil.ets二、为什么需要 Async/await—— 链式调用的最后痛点上一节用 Promise 链式调用解决了回调地狱但仍有两个痛点代码冗余多步异步操作需要嵌套.then逻辑越长代码越“碎”错误处理不够直观虽然catch能统一捕获但异步逻辑和错误处理分离阅读成本高条件判断麻烦链式调用中插入条件分支比如“商品库存不足则终止请求”会破坏链式结构。核心解决方案async/await是 Promise 的语法糖让异步代码“看起来像同步”逻辑更连贯。三、Async/await 核心语法异步代码“同步化”书写3.1 基础规则关键字作用核心说明async修饰函数1. 加了async的函数返回值自动包装为 Promise2. 函数内部才能使用awaitawait等待异步结果1. 只能在async函数内使用2. 等待 Promise 状态变为fulfilled/rejected3. 成功则返回结果失败则抛出错误需try/catch捕获3.2 代码实操Async/await 重构商品接口请求对比上一节的 Promise 链式调用用async/await重构“用户ID→用户详情→格式化”流程// ets/utils/PromiseUtil.ets新增asyncawaitDemo方法exportclassPromiseUtil{// 延续上一节的所有方法新增以下内容/** * Async/await 核心演示重构商品接口请求流程 */staticasyncasyncawaitDemo():Promisevoid{// async函数返回Promisevoidconsole.log( Async/await 演示开始 );// 复用上一节的Promise封装函数无需修改constgetUserIdPromise():Promisestring{returnnewPromise((resolve,reject){setTimeout((){constisSuccessMath.random()0.1;isSuccess?resolve(user_1001):reject(newError(获取用户ID失败));},1000);});};constgetUserDetailPromise(id:string):Promisestring{returnnewPromise((resolve,reject){setTimeout((){constisSuccessMath.random()0.1;isSuccess?resolve(ID:${id}姓名鸿蒙开发者):reject(newError(获取用户详情失败));},1000);});};constformatDetailPromise(detail:string):Promisestring{returnnewPromise((resolve,reject){setTimeout((){constisSuccessMath.random()0.1;isSuccess?resolve(【用户信息】${detail}):reject(newError(格式化详情失败));},1000);});};// 核心Async/await try/catch 处理 try{// 异步代码“同步化”书写无需.then嵌套constidawaitgetUserIdPromise();// 等待获取ID成功返回结果constdetailawaitgetUserDetailPromise(id);// 等待获取详情constresultawaitformatDetailPromise(detail);// 等待格式化console.log(Async/await 最终结果,result);// 成功流程}catch(error){// 捕获所有步骤的错误任意一步失败都会触发consterrMsg(errorasError).messageconsole.log(Async/await 错误,errMsg);}finally{// 无论成功/失败都会执行比如隐藏加载动画console.log(Async/await 流程结束清理资源);console.log( Async/await 演示结束 \n);}}}3.3 核心优势对比 Promise 链式调用逻辑连贯代码从上到下执行和同步代码逻辑一致无需嵌套.then错误处理直观try包裹成功流程catch统一捕获所有异步错误和同步错误处理方式一致条件判断友好可在await之间插入任意条件分支比如“ID为空则终止流程”不破坏代码结构try{constidawaitgetUserIdPromise();if(!id){console.log(用户ID为空终止流程);return;// 直接终止无需链式调用的return Promise.reject()}constdetailawaitgetUserDetailPromise(id);// ...后续逻辑}catch(error){/* 错误处理 */}四、Try/Catch/Finally异步错误处理的终极方案4.1 同步 异步错误的统一捕获try/catch不仅能捕获await的异步错误还能捕获同步代码错误实现“一站式”错误处理// ets/utils/PromiseUtil.ets补充错误处理演示staticasyncerrorHandleDemo():Promisevoid{console.log( 同步异步错误捕获演示开始 );try{// 同步错误比如调用未定义的函数// (window as any).undefinedFunc(); // 取消注释可测试同步错误// 异步错误Promise rejectconstidawaitgetUserIdPromise();// 10%概率失败constdetailawaitgetUserDetailPromise(id);// 同步错误比如类型错误constnum:numberdetail.length;// 正常无错模拟错误可改为 detail.lengthconsole.log(处理完成数字,num);}catch(error){// 统一捕获同步/异步错误都进入这里consterrMsg(errorasError).message;console.log(统一错误捕获,errMsg);}finally{console.log(无论成败都会执行的收尾逻辑);console.log( 错误捕获演示结束 \n);}}4.2 易错点提醒⚠️易错点1忘记给await加try/catch→ 异步错误会变成“未捕获Promise错误”导致程序崩溃⚠️易错点2await后面跟非Promise值 → 会自动包装为Promise.resolve(值)无报错但没必要⚠️易错点3async函数返回非Promise值 → 会自动包装为Promise比如async fn() { return 1; }等价于return Promise.resolve(1)。五、Promise 进阶并发AllSettled / Any上一节学了all等所有成功、race等第一个完成但真实业务中还有更多场景比如“批量请求商品数据无论成败都要统计结果成功多少、失败多少”“多源请求同一商品数据只要有一个成功就用哪个容错”。5.1 核心方法对比进阶方法适用场景核心特点Promise.allSettled批量任务全量结果统计比如批量商品接口请求1. 等待所有任务完成无论成功/失败2. 返回结果数组每个元素包含statusfulfilled/rejected和value/reasonPromise.any多源容错比如多CDN请求同一商品图片1. 等待第一个成功的任务2. 所有任务都失败才会reject返回AggregateError5.2 代码实操AllSettled Any 实战// ets/utils/PromiseUtil.ets新增promiseAdvancedDemo方法staticasyncpromiseAdvancedDemo():Promisevoid{console.log( Promise进阶并发allSettled/any演示开始 );// 模拟3个商品接口请求不同失败概率constgoodsApi1():Promisestring{returnnewPromise((resolve,reject){setTimeout(()Math.random()0.3?resolve(商品1数据):reject(newError(商品1请求失败)),1000);});};constgoodsApi2():Promisestring{returnnewPromise((resolve,reject){setTimeout(()Math.random()0.5?resolve(商品2数据):reject(newError(商品2请求失败)),1500);});};constgoodsApi3():Promisestring{returnnewPromise((resolve,reject){setTimeout(()Math.random()0.7?resolve(商品3数据):reject(newError(商品3请求失败)),800);});};// 1. Promise.allSettled批量商品请求结果统计constallSettledResultawaitPromise.allSettled([goodsApi1(),goodsApi2(),goodsApi3()]);console.log( Promise.allSettled 结果 );// 统计成功/失败数量constsuccessCountallSettledResult.filter(itemitem.statusfulfilled).length;constfailCountallSettledResult.filter(itemitem.statusrejected).length;console.log(成功${successCount}个失败${failCount}个);// 遍历结果allSettledResult.forEach((item,index){if(item.statusfulfilled){console.log(商品${index1}成功,item.value);}else{console.log(商品${index1}失败,(item.reasonasError).message);}});// 2. Promise.any多源容错取第一个成功的商品数据try{constanyResultawaitPromise.any([goodsApi1(),goodsApi2(),goodsApi3()]);console.log(\n Promise.any 结果 );console.log(第一个成功的商品数据,anyResult);}catch(error){// 所有任务都失败时触发AggregateErrorif(errorinstanceofAggregateError){console.log(\nPromise.any 所有请求失败,error.errors.map((e:Error)e.message));}}console.log( Promise进阶并发演示结束 \n);}5.3 关键说明allSettled结果结构// 成功项{status:fulfilled,value:商品1数据}// 失败项{status:rejected,reason:Error:商品2请求失败}any注意点优先返回第一个成功的结果即使有任务先失败比如商品3先失败商品1后成功仍返回商品1所有任务失败时抛出AggregateErrorerrors属性包含所有失败原因。六、避坑await 滥用导致并发变串行6.1 问题场景批量商品请求变成串行新手常犯的错误用for循环 await批量请求商品导致原本可并行的任务变成串行耗时翻倍// 错误示例串行请求总耗时11.50.83.3秒staticasyncbadawaitDemo():Promisevoid{console.log( 错误await滥用导致串行 );conststartDate.now();// 模拟3个商品请求复用上面的goodsApi1/2/3constresult1awaitgoodsApi1();// 等1秒constresult2awaitgoodsApi2();// 再等1.5秒constresult3awaitgoodsApi3();// 再等0.8秒consttotalTime(Date.now()-start)/1000;console.log(串行请求总耗时${totalTime.toFixed(1)}秒本该1.5秒);console.log( 串行演示结束 \n);}6.2 优化方案先创建Promise实例再批量await并行核心思路先触发所有异步请求创建Promise实例再用Promise.all等待所有结果实现并行// 正确示例并行请求总耗时最长任务1.5秒staticasyncgoodawaitDemo():Promisevoid{console.log( 正确先创建Promise再await并行 );conststartDate.now();// 第一步先触发所有请求创建Promise实例异步任务开始执行constpromise1goodsApi1();constpromise2goodsApi2();constpromise3goodsApi3();// 第二步批量等待所有结果并行总耗时最长任务时间constresultArrawaitPromise.all([promise1,promise2,promise3]);consttotalTime(Date.now()-start)/1000;console.log(并行请求总耗时${totalTime.toFixed(1)}秒符合预期);console.log( 并行演示结束 \n);}6.3 核心原则串行await写在循环/顺序执行中 → 任务依次执行总耗时相加并行先创建所有Promise实例触发异步任务再用Promise.all/allSettled等待 → 任务同时执行总耗时最长任务时间。七、入口页面扩展Index.ets补充新按钮触发本节的所有演示方法// ets/pages/Index.ets 完整代码import{PromiseUtil}from../utils/PromiseUtil;EntryComponentstruct Index{build(){Column({space:15}){Text(Promise异步进阶Async/await 进阶并发).fontSize(20).fontWeight(FontWeight.Bold).margin(20);// 上一节按钮保留不展示。实际可查看代码工程// 本节新增按钮Button(6. Async/await 核心演示).width(80%).height(50).onClick(()PromiseUtil.asyncawaitDemo());Button(7. 同步异步错误捕获).width(80%).height(50).onClick(()PromiseUtil.errorHandleDemo());Button(8. Promise.allSettled/any 进阶).width(80%).height(50).onClick(()PromiseUtil.promiseAdvancedDemo());Button(9. await避坑串行vs并行).width(80%).height(50).onClick(async(){awaitPromiseUtil.badawaitDemo();awaitPromiseUtil.goodawaitDemo();});}.width(100%).height(100%).justifyContent(FlexAlign.Center);}}八、核心总结Async/await 核心async函数返回 Promiseawait等待 Promise 结果结合try/catch可直观处理异步错误彻底替代链式调用Promise 进阶并发allSettled批量任务全量结果统计无论成败适合商品批量请求的结果汇总any多源容错取第一个成功的结果适合多CDN/多接口的容错场景await 避坑串行await写在顺序执行中 → 总耗时相加并行先创建所有 Promise 实例再用Promise.all等待 → 总耗时最长任务时间错误处理try/catch可统一捕获同步异步错误是鸿蒙异步开发的首选错误处理方式。九、代码仓库工程名称PromiseAsyncBasicDemo_1仓库地址https://gitee.com/juhetianxia321/harmony-os-code-base.git十、下节预告本节我们吃透了async/await语法糖和 Promise 进阶并发方案掌握了鸿蒙异步编程的核心能力。而实际开发中异步获取的商品、订单等数据几乎都以JSON 格式存在JSON 的解析、存储与读写能力是衔接异步数据和业务逻辑的关键桥梁。下一节我们将聚焦JSON核心基础与鸿蒙文件读写实战快速掌握JSON 核心语法规则与强类型接口匹配技巧ArkTSJSON模块序列化/反序列化方法rawfile只读目录与沙箱可读写目录的 JSON 操作JSON 处理避坑指南文件句柄关闭、异步顺序、预览器限制。通过本节学习你将打通“异步请求→JSON解析→本地存储”的完整链路为商品数据的本地缓存与业务处理做好准备。

更多文章