网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


前言

鸿蒙应用里,UI 的渲染和事件处理都在主线程上。如果在主线程里做重计算、大数组处理、复杂解析,就会把这一帧的时间占满,界面卡顿、点击没反应。要提升响应速度,就得把耗 CPU 的活放到别处算,算完再把结果拿回主线程更新界面——这离不开异步编程:用 Future、Promise 或 Callback 表示「稍后才有结果」的操作,主线程只负责发起任务和收到结果后的轻量处理,不堵在计算上。

下面从主线程为什么不能扛重算、鸿蒙里常见的异步写法、以及怎么用它们把 CPU 计算挪出去几方面说一下。

主线程和异步的关系

主线程要在一帧内完成布局、绘制和事件回调,一般只有十几毫秒的预算,才能维持 60 帧。一旦在这条线程上跑一段耗时循环、复杂排序、或同步读文件,这段时间内界面就动不了,用户会感觉卡甚至无响应。所以「优化 CPU 计算」在 UI 应用里,首先是指:别在主线程做重计算,改成在别的线程或任务里算,算完再通过异步回调或 Promise 把结果传回主线程,主线程只做「拿到结果、更新状态、触发重绘」这类轻量活。

异步模型的作用就是:让你「发起一个任务」和「处理结果」在时间上分开。发起任务时主线程立刻返回,不阻塞;等任务在后台线程执行完,通过 Callback 或 Future(Promise)把结果交回给你,你再在主线程里更新 UI。这样主线程的 CPU 负载下来了,响应速度就上去了。

鸿蒙里的 Future、Promise 与 Callback

鸿蒙 ArkTS/JS 里,异步通常用 Promiseasync/await 来表达「过一会儿才有结果」的操作。Promise 本质上就是一个「未来会完成」的占位:你调一个返回 Promise 的接口,马上拿到一个 Promise 对象,真正的结果要等异步任务完成后,通过 then 或 await 拿到。例如 TaskPool 的 execute() 返回的就是 Promise,你在主线程里 await taskpool.execute(task) 时,当前逻辑会挂起等待结果,但主线程本身不会被「算任务」占满——任务在池内线程执行,主线程在 await 处让出,可以处理别的消息或渲染,等 Promise resolve 后再继续执行后续代码,用结果更新 UI。

Callback 则是另一种常见写法:调用异步接口时传入一个回调函数,任务完成后由系统或框架在合适的时机(往往是主线程)调用你的回调,把结果传进来。很多 ArkTS/鸿蒙 API 既支持 Promise 也支持 Callback,例如某些异步接口会提供 successfail 回调参数。用 Callback 时要注意:回调里如果要做 UI 更新,要确保回调执行在主线程;若回调在 Worker 或 TaskPool 的线程里被调用,需要把「更新 UI」再抛回主线程(例如通过主线程的 Task 或消息),避免在非 UI 线程直接改界面导致异常或闪烁。

简单说:用 Promise/async 或 Callback 把「计算」和「拿到结果」解耦;计算在后台做,结果通过回调或 resolve 回到主线程再更新界面

用异步任务分担 CPU 计算

鸿蒙里真正把 CPU 计算挪到非主线程的,主要是 TaskPoolWorker。两者都支持异步模型:TaskPool 的 execute() 返回 Promise,你可以在主线程里 await.then() 取结果;Worker 通过 postMessage 发任务、在 onmessage 里收结果,本质上也是「异步:先发请求,后收回调」。

典型用法是:把一段纯计算(例如大数组过滤、排序、JSON 解析、加解密、图片处理)封装成 Task 或 Worker 里的处理函数,在主线程调用 taskpool.execute(task) 或向 Worker 发消息,不在这条调用链里做耗时运算。任务在池内线程或 Worker 线程执行,主线程继续响应用户操作;任务完成后通过 Promise 的 resolve 或 Worker 的 onmessage 把结果传回,你在回调里更新状态、刷新列表或弹窗,界面就只做「结果展示」,不做「重算」。

注意一点:在 Promise 的 then 或 async/await 的后续代码里,如果运行在主线程,可以直接更新 UI;如果框架在某些情况下在非主线程调用你的 then,就要先派发到主线程再更新 UI。鸿蒙文档里会说明各 API 的回调线程,用的时候留意一下,避免在错误线程改 UI。

下面用 TaskPool + Promise 举一个「主线程发起、池内计算、结果回主线程」的写法示例(仅作理解用,具体 API 以官方文档为准):

import taskpool from '@ohos.taskpool';

// 把耗时计算封装成 Task,在池内线程执行
function doHeavyWork(data: number[]): Promise<number> {
  const task = new taskpool.Task(() => {
    let sum = 0;
    for (let i = 0; i < data.length; i++) {
      sum += data[i];
    }
    return sum;
  });
  return taskpool.execute(task) as Promise<number>;
}

// 在页面或业务里:async 函数内 await,主线程不堵在循环上
async loadData() {
  this.loading = true;
  const result = await doHeavyWork(this.rawArray);
  this.total = result;
  this.loading = false;
}

解析:doHeavyWork 里用 taskpool.Task 包住一段纯计算(这里用求和举例),taskpool.execute(task) 把任务丢到池内线程执行并返回 Promise。主线程调用 await doHeavyWork(...) 时,不会在 for 循环里占满 CPU,而是等 Promise 在后台 resolve 后再继续执行 this.total = result 和关闭 loading。这样「算」在池里、「更新状态」在主线程,界面就不会因为这段计算而卡顿。实际项目中把循环换成排序、解析、加解密等即可,模式一样。

实际场景里怎么用

场景一:列表数据过滤、排序
用户输入关键词或选择排序方式后,如果列表很大,在主线程做 filter、sort 会卡。可以先把当前列表和条件交给 TaskPool 执行过滤或排序,主线程显示 loading;等 Promise 返回结果,再在主线程把结果赋给列表数据源并刷新列表。这样输入和滑动始终流畅。

场景二:大 JSON 或配置解析
启动或切换 Tab 时要解析一大段 JSON,可以放到 TaskPool 或 Worker 里解析,解析完通过 Promise 或 onmessage 把解析后的对象传回主线程,主线程只做「拿到对象、写入状态、触发界面更新」。避免在首屏或切换时主线程被解析占满。

场景三:图片处理、编码解码
缩略图生成、格式转换、简单滤镜等,都适合放进 TaskPool 的一次性任务里,用 Promise 取结果后再把图片数据或路径交给 UI 显示。主线程不参与像素级计算,只负责「收到结果后把图贴上去」。

场景四:多处轻量计算并发
例如首页要同时算「统计汇总」「推荐列表的预处理」「缓存校验」,可以分别提交多个 Task 到 TaskPool,用 Promise.all 等所有结果再在主线程统一更新。这样多个小任务在池内并行,主线程只等一轮汇总,比串行在主线程算要快、也更不卡。

小结

利用 HarmonyOS 的异步模型(Future/Promise、Callback)优化 CPU 计算,核心就是:主线程只做 UI 和发起/收尾,重计算交给 TaskPool 或 Worker,通过 Promise 或回调拿结果再更新界面。写的时候注意:耗 CPU 的逻辑不要写在主线程的同步调用里;用 TaskPool 做短平快的离散任务、用 Worker 做长时或隔离逻辑;拿到结果的回调里若不在主线程,要先派发到主线程再改 UI。这样主线程负载下来,应用响应速度自然会上去。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐