Hello,兄弟们,我是 V 哥!
还记得以前写 Android 或者早期 JavaScript 的时候,那个传说中的**"回调地狱"**吗?
javascript
// 伪代码演示:让人崩溃的金字塔
login(user, (res1) => {
getUserInfo(res1.id, (res2) => {
getOrders(res2.token, (res3) => {
getDetail(res3.orderId, (res4) => {
// 终于结束了... 代码已经缩进到屏幕外边了
})
})
})
})
这种代码,维护起来简直是噩梦!但在鸿蒙 ArkTS 的 API 21 环境下,兄弟们千万别再这么写了!ArkTS 是基于 TypeScript 的,它原生支持非常强大的 async/await 语法。
今天 V 哥就带你把这段"金字塔"拍平,用 同步的逻辑写异步的代码,优雅得像喝下午茶一样!
核心心法:把"等待"变成"暂停"
兄弟们,记住 V 哥这两个口诀:
async:加在函数定义前面,表示"这里面有耗时的活儿"。await:加在耗时的调用前面,表示"等着这儿干完,再去干下一行,但别把界面卡死"。
有了这两个神器,异步代码写出来就像在写小学作文,从上到下,一行一行读,逻辑清晰无比。
实战代码案例
为了让大家直观感受,V 哥写了一个完整的 Demo。咱们模拟三个常见的真实场景:
- 串行执行:先登录,再拿用户信息。
- 并发执行:同时拉取"广告配置"和"首页推荐"。
- 异常处理:优雅地捕获网络错误。
操作步骤: 打开你的 DevEco Studio 6.0,新建一个 ArkTS 页面,把下面的代码完整复制进去,直接运行!
typescript
import promptAction from '@ohos.promptAction';
/**
* V哥的模拟网络请求类
* 在真实项目中,这里会换成 httpRequest 或者 网络库
*/
class NetworkSimulator {
// 模拟一个异步耗时操作,返回 Promise
static request(apiName: string, data: string, delay: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 模拟 20% 的概率失败
if (Math.random() < 0.2) {
reject(new Error(`${apiName} 请求失败,网络不给力!`));
} else {
resolve(`${apiName} 返回的数据: ${data}`);
}
}, delay);
});
}
}
@Entry
@Component
struct AsyncAwaitDemo {
@State resultLog: string = 'V哥准备好输出日志了...';
@State isLoading: boolean = false;
build() {
Column() {
Text('鸿蒙 async/await 实战实验室')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 30, bottom: 20 })
// 场景一:串行执行
Button('场景1:串行执行 (登录 -> 获取信息)')
.width('90%')
.margin({ bottom: 15 })
.onClick(() => {
this.testSequential();
})
// 场景二:并发执行
Button('场景2:并发执行 (同时拉取配置和广告)')
.width('90%')
.margin({ bottom: 15 })
.onClick(() => {
this.testParallel();
})
// 场景三:异常捕获
Button('场景3:异常捕获 (模拟失败重试)')
.width('90%')
.margin({ bottom: 15 })
.onClick(() => {
this.testErrorHandling();
})
// 日志显示区域
Column() {
Text(this.resultLog)
.fontSize(14)
.fontColor('#333333')
.width('100%')
}
.width('90%')
.height('40%')
.padding(15)
.backgroundColor('#F1F3F5')
.borderRadius(10)
.margin({ top: 20 })
if (this.isLoading) {
LoadingProgress()
.width(30)
.height(30)
.margin({ top: 20 })
.color(Color.Blue)
}
}
.width('100%')
.height('100%')
.padding({ left: 20, right: 20 })
}
/**
* V哥解析:场景1 - 串行执行
* 特点:一步接一步,下一步依赖上一步的结果。
* 代码逻辑:完全是线性的,像同步代码一样易读!
*/
async testSequential() {
this.isLoading = true;
this.resultLog = '1. 开始登录...\n';
try {
// V哥重点:await 会暂停函数执行,直到 Promise resolve
// 这里模拟先登录,耗时 1000ms
let loginRes = await NetworkSimulator.request('LoginAPI', 'Token123', 1000);
this.resultLog += ` ${loginRes}\n`;
this.resultLog += '2. 正在获取用户信息...\n';
// 依赖上面的 Token,继续 await
let userRes = await NetworkSimulator.request('GetUserInfo', 'V哥的大名', 800);
this.resultLog += ` ${userRes}\n`;
this.resultLog += '✅ 全部完成!(串行总耗时约 1.8s)';
promptAction.showToast({ message: '串行执行完成' });
} catch (error) {
this.resultLog += `❌ 出错了: ${error.message}`;
} finally {
this.isLoading = false;
}
}
/**
* V哥解析:场景2 - 并发执行
* 特点:两个请求互不依赖,同时发出,谁先回来谁先结束。
* 优势:速度最快!总耗时 = 两个请求中最慢的那个,而不是两者之和。
*/
async testParallel() {
this.isLoading = true;
this.resultLog = '1. 同时启动多个任务...\n';
// 记录开始时间
const startTime = Date.now();
try {
// V哥重点:Promise.all()
// 把所有要并发的 Promise 放进数组里
// await 会等数组里所有的 Promise 都 resolve 才继续
let results = await Promise.all([
NetworkSimulator.request('GetConfig', '系统配置', 1500), // 假设这个慢
NetworkSimulator.request('GetBanner', '广告图片', 1000) // 假设这个快
]);
// results 是一个数组,顺序和你传入的顺序一致,不管谁先回来
this.resultLog += ` ${results[0]}\n`; // 第一个结果
this.resultLog += ` ${results[1]}\n`; // 第二个结果
const duration = Date.now() - startTime;
this.resultLog += `✅ 全部完成!(并发总耗时约 ${duration}ms,比串行快!)`;
promptAction.showToast({ message: '并发执行完成' });
} catch (error) {
this.resultLog += `❌ 出错了: ${error.message}`;
} finally {
this.isLoading = false;
}
}
/**
* V哥解析:场景3 - 异常处理
* 特点:async/await 下,我们用 try...catch...finally 代替 .then().catch()
* 这比传统的 Promise 链式调用要直观得多,像处理 Java 异常一样舒服。
*/
async testErrorHandling() {
this.isLoading = true;
this.resultLog = '尝试发送请求 (模拟20%失败率)...\n';
try {
// 这里的请求可能会抛出 Error
let data = await NetworkSimulator.request('RiskyAPI', '试试运气', 1000);
this.resultLog += ` 成功: ${data}`;
promptAction.showToast({ message: '请求成功' });
} catch (error) {
// V哥重点:一旦任何一步 await 报错,直接跳进 catch
this.resultLog += ` 捕获到异常: ${error.message}\n`;
this.resultLog += ` 这里可以进行重试逻辑...`;
promptAction.showToast({ message: '请求被拦截' });
} finally {
// V哥重点:finally 无论成功失败都会执行
// 适合用来关闭 Loading 弹窗
this.isLoading = false;
}
}
}
运行结果:

V 哥的代码深度解析
兄弟们,代码能跑了,咱们得懂原理,不然面试的时候要挂!
1. 为什么 async/await 不会卡死界面?
这就是并发编程的魔力。 当你写 let res = await someRequest() 的时候,ArkTS 的运行时会把当前任务的挂起,把主线程的控制权交还给 UI 系统。 这就好比你去排队买奶茶,你叫服务员做奶茶(发起请求),你站在旁边等(await),但**店里的其他人(UI线程)**依然可以继续进店买东西。只有当你的奶茶好了(Promise resolve),你才拿着奶茶走人(代码继续往下走)。
2. Promise.all 是性能优化的利器
在场景 2 中,V 哥演示了 Promise.all。 如果你的首页有 5 个接口,互不依赖,你千万别写 5 行 await:
typescript
// ❌ 错误写法:慢得要死
let a = await req1(); // 等1秒
let b = await req2(); // 再等1秒
// ... 总耗时 5秒
typescript
// ✅ V 哥正确写法:飞快
let results = await Promise.all([req1(), req2(), req3(), req4(), req5()]);
// 总耗时 = 最慢的那个接口 (假设是 1.2秒)
这可是实打实的性能提升,用户打开 App 的速度直接肉眼可见变快!
3. 不要忘记了 try-catch
以前写 Promise 链,如果不加 .catch(),报错了可能就像石沉大海,静默失败。 用了 async/await,一定要 包裹在 try...catch 里。这是对自己代码负责,也是对用户负责。
总结
来来来,V 哥稍微小结一下:
- 逻辑复杂? 用
async/await拍平金字塔。 - 请求多且慢? 用
Promise.all并行加速。 - 怕出错? 用
try/catch稳稳兜底。
在 DevEco Studio 6.0 里,这套组合拳用熟练了,你的代码质量和开发效率绝对能甩开同行一条街。
我是 V 哥,拒绝回调地狱,从今天开始!咱们下期见!👋