HarmonyOS ArkTS 异步编程:async/await 从入门到精通
2026/6/9 1:13:54 网站建设 项目流程

文章目录

    • 前言
    • 一、什么是异步?为什么需要异步?
      • 1.1 同步 vs 异步的直观理解
      • 1.2 回调函数(不推荐)
    • 二、Promise:回调地狱的第一个解法
      • 2.1 Promise 基础
      • 2.2 Promise.all:并发执行多个异步任务
    • 三、async/await:优雅的异步写法
      • 3.1 基本语法
      • 3.2 本项目中的 async/await 实战
    • 四、错误处理:try/catch 捕获异步错误
      • 4.1 async/await 与 try/catch
      • 4.2 本项目错误处理模式
    • 五、综合实战:带完整错误处理的地图初始化
      • 5.1 可运行示例
    • 六、async/await 常见误区
    • 总结

前言

在 HarmonyOS 开发中,几乎所有涉及I/O 操作(网络请求、文件读写、定位获取)的 API 都是异步的。如果你还在用回调函数写异步代码,代码会越来越难维护;而async/await让异步代码"长得像同步代码",逻辑清晰,错误处理简单。

本篇通过本项目中的真实异步场景,带你彻底掌握 ArkTS 的异步编程模型。

一、什么是异步?为什么需要异步?

1.1 同步 vs 异步的直观理解

同步(排队等待): 你去餐厅点餐 → 站在柜台等 → 厨师做好 → 你拿到餐 → 你去找座位 异步(边等边做): 你去餐厅点餐 → 拿到取餐号 → 你去找座位(继续做其他事)→ 号码叫到了 → 去取餐

在程序中,“等待"的时间(网络请求、GPS定位)如果是同步阻塞的,整个 UI 线程被卡住,用户看到的是"无响应的卡顿界面”。异步让耗时操作在后台执行,UI 线程保持响应。

1.2 回调函数(不推荐)

最早的异步写法是回调函数,但会导致"回调地狱":

// 回调地狱(可读性极差)getLocation((location)=>{convertCoordinate(location,(gcj02)=>{moveCamera(gcj02,()=>{addMarkers(gcj02,()=>{showSheet(()=>{// 越来越深...});});});});});

二、Promise:回调地狱的第一个解法

2.1 Promise 基础

// Promise 的三种状态:pending(等待中)→ fulfilled(成功)/ rejected(失败)functiongetLocationPromise():Promise<{lat:number,lng:number}>{returnnewPromise((resolve,reject)=>{// 模拟异步定位setTimeout(()=>{constsuccess=Math.random()>0.2;// 80% 概率成功if(success){resolve({lat:39.9042,lng:116.4074});// 成功:resolve 传结果}else{reject(newError('定位失败,请检查权限'));// 失败:reject 传错误}},1000);});}// 使用 PromisegetLocationPromise().then((location)=>{console.log(`定位成功:${location.lat},${location.lng}`);returnlocation;// 可以链式传递}).then((location)=>{console.log('开始移动地图...');}).catch((error)=>{console.error(`错误:${error.message}`);}).finally(()=>{console.log('无论成功失败都执行');});

2.2 Promise.all:并发执行多个异步任务

// 同时发起多个请求,等所有请求都完成asyncfunctionloadAllData():Promise<void>{try{const[location,stationList,userInfo]=awaitPromise.all([getLocationPromise(),fetchStationList(),fetchUserInfo()]);// 三个请求全部完成后才到这里console.log('所有数据加载完成');}catch(error){console.error('其中一个请求失败了');}}// Promise.race:谁先完成用谁(竞速)asyncfunctiongetLocationWithTimeout():Promise<void>{consttimeout=newPromise<never>((_,reject)=>setTimeout(()=>reject(newError('定位超时')),5000));try{constlocation=awaitPromise.race([getLocationPromise(),timeout]);console.log('定位成功',location);}catch(error){console.error('定位超时或失败');}}

三、async/await:优雅的异步写法

3.1 基本语法

// async 函数:返回值自动包装成 PromiseasyncfunctionfetchGasStations():Promise<StationInfo[]>{// await 等待 Promise 完成,像同步一样获取结果constlocation=awaitgetMyLocation();// 等待定位constgcj02=awaitconvertToGCJ02(location);// 等待坐标转换conststations=awaitqueryNearbyStations(gcj02);// 等待查询returnstations;}// 调用 async 函数fetchGasStations().then(stations=>console.log(stations)).catch(err=>console.error(err));// 或者用 await 调用(必须在另一个 async 函数内)asyncfunctionmain():Promise<void>{conststations=awaitfetchGasStations();console.log(stations);}

3.2 本项目中的 async/await 实战

MapUtil.ets中的连锁异步操作:

// 异步获取当前位置并移动地图asyncmoveToMyLocation(mapController:map.MapComponentController):Promise<void>{// 第一步:await 等待定位完成letlocation:geoLocationManager.Location=awaitthis.getMyLocation();// 第二步:将位置设置到地图(同步)mapController?.setMyLocation(location);// 第三步:await 等待坐标转换完成letgcj02Position=awaitthis.convertToGCJ02(location.latitude,location.longitude);// 第四步:移动地图相机(同步)this.moveToCurrentPosition(gcj02Position.latitude,gcj02Position.longitude,mapController);}

没有 async/await,这段代码用回调写会非常复杂。

四、错误处理:try/catch 捕获异步错误

4.1 async/await 与 try/catch

asyncfunctionsafeGetLocation():Promise<void>{try{constlocation=awaitgeoLocationManager.getCurrentLocation();console.log(`纬度:${location.latitude}, 经度:${location.longitude}`);}catch(error){// 无论是定位权限被拒绝、GPS未开启还是超时,都在这里处理if(error.code===201){console.error('没有定位权限,请在设置中开启');}elseif(error.code===3301000){console.error('位置功能未开启');}else{console.error(`定位失败:code=${error.code}, msg=${error.message}`);}}finally{// 无论成功还是失败,都隐藏 Loadingthis.isLoading=false;}}

提示:在 async 函数中,如果 await 的 Promise 被 reject 且没有被 try/catch 包裹,错误会继续向上抛出,导致未捕获的 Promise 拒绝(程序崩溃)。永远要对可能失败的 await 加 try/catch

4.2 本项目错误处理模式

GasStationPage.ets的地图初始化回调:

this.callback=async(err,mapController):Promise<void>=>{if(err){// 错误优先处理Logger.error('testTag',`init fail, code:${err.code}, message:${err.message}`);return;// 提前返回,不继续执行}// 成功后的逻辑this.mapController=mapController;// ...};

这是AsyncCallback 模式(HarmonyOS 特有),通过回调的第一个参数err判断是否出错。

五、综合实战:带完整错误处理的地图初始化

5.1 可运行示例

import{geoLocationManager}from'@kit.LocationKit';import{map,mapCommon}from'@kit.MapKit';// 模拟数据类型interfaceLocationResult{latitude:number;longitude:number;}// 封装定位工具(完整错误处理版)classLocationService{// 获取当前位置(带超时控制)staticasyncgetCurrentPosition(timeoutMs:number=5000):Promise<LocationResult>{returnnewPromise((resolve,reject)=>{// 超时控制consttimer=setTimeout(()=>{reject(newError(`定位超时(${timeoutMs}ms)`));},timeoutMs);geoLocationManager.getCurrentLocation().then((location)=>{clearTimeout(timer);resolve({latitude:location.latitude,longitude:location.longitude});}).catch((err:Error)=>{clearTimeout(timer);reject(err);});});}// 带重试的定位staticasyncgetCurrentPositionWithRetry(maxRetries:number=3,retryDelay:number=1000):Promise<LocationResult>{letlastError:Error=newError('未知错误');for(letattempt=1;attempt<=maxRetries;attempt++){try{constlocation=awaitLocationService.getCurrentPosition(5000);console.log(`${attempt}次定位成功`);returnlocation;}catch(error){lastError=errorasError;console.warn(`${attempt}次定位失败:${lastError.message}`);if(attempt<maxRetries){// 等待后重试awaitnewPromise<void>(resolve=>setTimeout(resolve,retryDelay));}}}thrownewError(`定位失败(已重试${maxRetries}次):${lastError.message}`);}}// 在组件中使用@Entry@Componentstruct AsyncDemoPage{@Statestatus:string='等待定位…';@Statelatitude:number=0;@Statelongitude:number=0;@StateisLoading:boolean=false;@StateerrorMsg:string='';asyncstartLocation():Promise<void>{this.isLoading=true;this.errorMsg='';this.status='正在定位…';try{// 带重试的定位constlocation=awaitLocationService.getCurrentPositionWithRetry(3,1000);this.latitude=location.latitude;this.longitude=location.longitude;this.status='定位成功!';}catch(error){this.errorMsg=(errorasError).message;this.status='定位失败';}finally{this.isLoading=false;}}build(){Column({space:20}){Text('异步定位演示').fontSize(20).fontWeight(FontWeight.Bold)// 状态显示Column({space:8}){Text(this.status).fontSize(16).fontColor(this.errorMsg?'#FF4D4F':'#333333')if(this.latitude!==0){Text(`纬度:${this.latitude.toFixed(6)}`).fontSize(14).fontColor('#666666')Text(`经度:${this.longitude.toFixed(6)}`).fontSize(14).fontColor('#666666')}if(this.errorMsg){Text(this.errorMsg).fontSize(12).fontColor('#FF4D4F').padding(8).backgroundColor('#FFF2F0').borderRadius(8)}}.padding(16).width('90%').backgroundColor('#F8F9FA').borderRadius(12)if(this.isLoading){LoadingProgress().width(40).height(40).color('#1A6FF5')}else{Button('开始定位(带重试)').onClick(()=>{this.startLocation();}).width('80%').height(48).backgroundColor('#1A6FF5').fontColor('#FFFFFF').borderRadius(24)}}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}

六、async/await 常见误区

误区正确做法
在非 async 函数中用 await函数必须声明为async
忘记 await 导致拿到 Promise 对象const x = await somePromise()
多个独立 await 串行执行浪费时间Promise.all([a(), b()])并发
忘记 try/catch 导致崩溃所有可能失败的 await 都加 try/catch
for...of中 await 正确,forEach中不生效异步循环用for...of,不用forEach
// ❌ 错误:forEach 中的 await 不等待stations.forEach(async(station)=>{awaitaddMarker(station);// 这些 await 并发执行,顺序不保证});// ✅ 正确:for...of 中的 await 真正串行等待for(conststationofstations){awaitaddMarker(station);// 一个加完再加下一个}// ✅ 更好:并发执行(如果顺序不重要)awaitPromise.all(stations.map(station=>addMarker(station)));

总结

async/await是 ArkTS 处理异步操作的最佳实践:让异步代码"看起来像同步",配合try/catch进行错误处理,配合Promise.all实现并发优化。本项目中的定位、坐标转换、地图初始化都大量使用了 async/await,理解这些模式是读懂项目源码的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询