HarmonyOS 5.0.0 TaskPool 进度回传怎么写稳:Sendable、批次号和取消状态怎么拆

HarmonyOS 5.0.0 TaskPool 进度回传怎么写稳:Sendable、批次号和取消状态怎么拆
这个问题看起来像一个小细节,真正落到 HarmonyOS 5.0.0 以上的应用里,会牵出页面状态、生命周期、异常兜底和多设备适配几条线。我的处理方式是先把问题复现出来,再看哪一层负责,最后把能复用的部分封装起来。
我不会把它写成官方概念解释。概念只解决“知道是什么”,但开发时更常见的是:代码能跑,边界一来就乱。所以这篇按排查过程来讲,重点放在怎么复现、怎么拆方案、怎么验证。
问题先复现出来
耗时任务放进 TaskPool 后,页面最常见的问题不是任务跑不动,而是进度回来的时机不稳定。用户切换页面、重新发起任务、取消上一轮任务时,旧进度还可能继续改 UI,最后表现成进度条倒退、按钮状态错乱、结果列表被旧任务覆盖。
我一般会先做两个最小案例。第一个案例只保留问题本身,方便确认是不是框架能力用错;第二个案例加上工程边界,看看这个写法能不能放进真实项目里长期维护。
案例一:最小复现
先用一个批量解析任务复现进度回传。每次任务启动都生成批次号,页面只接收当前批次的数据。
@Concurrent
async function parseBatch(batchId: number, total: number): Promise<Array<number>> {
let result: Array<number> = []
for (let i = 0; i < total; i++) {
result.push(i)
}
return result
}
class PageTaskState {
currentBatch: number = 0
async start() {
const batch = Date.now()
this.currentBatch = batch
const data = await taskpool.execute(parseBatch, batch, 2000) as Array<number>
if (this.currentBatch !== batch) { return }
console.info('只接收当前批次结果', data.length)
}
}
这段代码的重点不是行数,而是把触发条件写清楚。只要能稳定复现,后面判断问题就不会靠猜。这里我会观察三件事:状态有没有按预期变化,异常分支有没有被吃掉,页面离开后还有没有旧回调。
案例二:加上工程边界
第二个案例加入取消状态和 Sendable 数据结构,避免跨线程传普通对象时出现隐式复制和状态不一致。
@Sendable
class ProgressMessage {
batchId: number = 0
done: number = 0
total: number = 0
canceled: boolean = false
}
class ProgressGuard {
private active = 0
nextBatch(): number { this.active = Date.now(); return this.active }
accept(msg: ProgressMessage): boolean {
return msg.batchId === this.active && !msg.canceled
}
}
第二个案例比第一个更接近工程写法。它多出来的不是复杂度,而是边界:重复进入页面、任务被取消、窗口切换、资源失败、旧数据回写,这些都是线上更容易遇到的问题。
几种方案怎么选
| 方案 | 适合场景 | 问题 | 我的选择 |
|---|---|---|---|
| 临时写在页面里 | 只有一个页面用 | 很容易漏释放或漏兜底 | 只适合验证想法 |
| 每个页面各写一套 | 页面差异很大 | 重复代码多,后期不好查 | 不推荐长期用 |
| 抽成小工具或状态对象 | 多页面、多设备、可复用场景 | 要多设计一层边界 | 推荐 |
我的判断标准很简单:如果这个能力只影响一个按钮,可以放在页面里;如果它会影响页面结构、数据回写、资源释放或者审核材料,就应该收口。HarmonyOS 应用后面要适配的设备形态会越来越多,把边界提前拆清楚,比上线后补丁式修复要稳。
验证清单
验证时我会按下面这几步走:第一,冷启动进入页面,看默认状态是否正确;第二,触发问题场景,看状态是否能恢复;第三,快速退出再进入,看旧任务是否还会回调;第四,模拟失败分支,看用户是否有兜底提示;第五,保留一张截图或日志,方便后续回看。
这几个动作看起来普通,但能挡住大部分“本地没问题、换设备就不稳”的情况。尤其是窗口化、折叠屏、多任务、后台恢复这些场景,靠肉眼点两下是不够的。
可以怎么封装复用
TaskPool 相关逻辑适合封装成 TaskGuard。它不负责具体任务,只负责批次号、取消标记、结果接收判断。页面只关心当前任务是否还能回写,这样搜索、图片处理、导入解析都能复用同一套边界。
封装时我会刻意少做一点,不会一上来做成大框架。只保留三个入口:start、update、release。start 负责注册和初始化,update 负责接收变化,release 负责释放资源。这样后面无论换成页面监听、任务队列还是资源加载,都能沿着同一套检查方式排查。
以后怎么避免
这类问题最怕写完就算结束。我的做法是把它写进页面检查清单:有没有复现步骤,有没有两个案例,有没有失败兜底,有没有释放动作,有没有跨设备或窗口变化验证。只要其中一项缺失,就先别急着合并。
这篇对应的官方知识点可以继续往外扩:生命周期负责资源边界,ArkUI 状态负责界面刷新,TaskPool 或异步任务负责耗时逻辑,AGC 审核材料负责最终上架解释。把这些边界连起来,文章才不是空讲概念,代码也更经得起后续维护。
更多推荐

所有评论(0)