资讯详情

ArkTS Array+Map:离线导出队列优先级老化与撤销后重排【鸿蒙心迹】

📅 2026/10/14 23:23:32 | 华诺云谱 👁 阅读
ArkTS Array+Map:离线导出队列优先级老化与撤销后重排【鸿蒙心迹】
素材说明本文配套封面、DevEco Studio、模拟器、手机界面及 HiLog 全为人工制作的技术演示未在真实 HarmonyOS SDK 下编译、真机执行或完成审核测试。任务与数值均为可复核的示例不是外部服务的真实返回值。一、排在第一位的任务未必应该永远先走一次处理离线报表导出队列时我发现用户真正抱怨的并不是任务慢而是“早提交的任务一直没有轮到”。给每种导出类型设置一个固定优先级当然很简单紧急任务总是排在普通任务前面。但只要高优先级任务源源不断进来一个最早排队的低优先级任务就可能永远没有执行机会。这不是数组排序写错而是调度策略本身没有描述等待成本。本篇采用FairExportDemo页面为ExportQueuePage演示任务FQ-3412。它只有五个离线导出请求足够把规则讲清楚当前时钟不是系统毫秒时间而是应用产生的一次次逻辑轮次在演示的tick8时按每等待三轮增加一档的办法计算有效优先级最高封顶五档。取消T-02后队头应当变成T-05状态记为NEXT_T05。这样设计的目的就是让图、代码和可手算的数据可以相互校验。如果直接修改任务本身的basePriority下一轮会再拿修改后的数值老化相当于等待成本被重复计入。我更倾向于保留提交时的基础权重和入队轮次把有效优先级当作当下计算出来的派生值。这样排序失败也不会损坏原始任务页面切走后重新进入仍然能够解释为什么某个任务排到了前面。二、先定字段含义再谈 Array.sort 的结果对于小批量离线任务一个普通Array就能容纳待调度快照Map可以维护按 ID 查找的业务索引。这里没有调用 HarmonyOS 特有的任务调度服务采用的是 ArkTS 可表达的应用层策略所以不能把它包装成系统自带“公平队列 SDK”。示例字段只保留id、base、arrivedTick、sequence与status。sequence是同一轮到达时仍然可以稳定排序的入队序号不能借助当前数组下标代替。这段代码解决基础权重被重复改写的问题有效分数由当前轮次重算老化只在显示或决策时发生Math.max防止时钟倒退导致负等待Math.min保证最高五档。业务需要先保证nowTick是单调推进的有效整数外部时间戳不能随意混入。interfaceExportJob{id:string;base:number;arrivedTick:number;sequence:number;status:PENDING|CANCELLED;}functioneffectivePriority(job:ExportJob,nowTick:number):number{constwaitedMath.max(0,nowTick-job.arrivedTick);returnMath.min(5,job.baseMath.floor(waited/3));}constinitialJobs:ExportJob[][{id:T-01,base:1,arrivedTick:0,sequence:1,status:PENDING},{id:T-02,base:3,arrivedTick:4,sequence:2,status:PENDING},{id:T-03,base:2,arrivedTick:3,sequence:3,status:PENDING},{id:T-04,base:1,arrivedTick:1,sequence:4,status:PENDING},{id:T-05,base:3,arrivedTick:5,sequence:5,status:PENDING}];逐个算就能得到结论。在第八轮T-01是三档、T-02是四档、T-03是三档、T-04是三档、T-05是四档。还没有取消时T-02和T-05同为四档较早入队的T-02应先执行。用户主动撤销后T-02不能再参与候选比较此时T-05理应站到最前。这里不使用百分比进度来掩盖结果五条原始任务里一条撤销剩余四条待调度。三、取消不是删除数组元素更不是对正在执行的任务施法我会把取消分成两种语义等待中撤销标记后从下一次候选快照里排除运行中取消则必须通知真实执行器并等待明确的终止结果。本文只覆盖第一种状态避免写出一个“改成 CANCELLED 就能终止文件写入”的错误示例。离线队列要留下撤销记录方便排查用户为什么看不到这份导出结果而不是在内存里把它彻底抹掉。这段代码解决撤销后的重排先按 ID 制作不可变的新快照然后只从PENDING中挑候选。比较器顺序是有效优先级降序、到达轮次升序、稳定序号升序即便引擎的稳定排序行为不同也有确定的最终比较次序。functioncancelWaitingJob(jobs:ExportJob[],targetId:string):ExportJob[]{returnjobs.map((job:ExportJob):ExportJob{if(job.id!targetId||job.status!PENDING)returnjob;return{id:job.id,base:job.base,arrivedTick:job.arrivedTick,sequence:job.sequence,status:CANCELLED};});}functiondispatchOrder(jobs:ExportJob[],nowTick:number):ExportJob[]{constcandidatesjobs.filter((job:ExportJob)job.statusPENDING);returncandidates.sort((a:ExportJob,b:ExportJob):number{constbyScoreeffectivePriority(b,nowTick)-effectivePriority(a,nowTick);if(byScore!0)returnbyScore;if(a.arrivedTick!b.arrivedTick)returna.arrivedTick-b.arrivedTick;returna.sequence-b.sequence;});}由于排序发生在filter产生的候选数组上原始队列的审计顺序并没有被改写。如果后续加入“执行中”“失败待重试”状态只需要在状态机层决定哪些状态可以参与选取不能直接把所有非取消状态都视为待调度。还要警惕重试把arrivedTick清零那会让曾经等待很久的任务被当成新任务。应由产品明确重试继承原等待成本还是进入新的服务等级再决定规则。四、为什么我不在每次渲染时直接重排原数组ArkUI 页面的状态变更会触发显示更新但渲染时如果顺手给同一个数组做原地排序就容易发生显示顺序、审计顺序和业务索引一起变化。这个 Demo 把dispatchOrder定义成纯函数状态变化时输入确定的任务快照和tick8返回一个独立有序列表。显示层展示前四个等待任务点击取消T-02之后重新计算队头图片只展示被取消后的结果。这段代码解决页面读取与显示分离示例使用Map计算稳定 ID 索引并给界面提供一个可重复计算的快照。它不会自动执行文件导出也不会假称已经关联系统服务任何耗时导出都应由真实队列执行器另行管理。interfaceQueueView{nextId:string;pending:number;cancelled:number;state:string;}functionbuildQueueView(jobs:ExportJob[],nowTick:number):QueueView{constbyIdnewMapstring,ExportJob();jobs.forEach((job:ExportJob)byId.set(job.id,job));if(byId.size!jobs.length){return{nextId:,pending:0,cancelled:0,state:DUPLICATE_ID};}constordereddispatchOrder(jobs,nowTick);constcancelledjobs.filter((j:ExportJob)j.statusCANCELLED).length;constnextIdordered.length0?ordered[0].id:;return{nextId,pending:ordered.length,cancelled,state:nextId.length0?NEXT_${nextId.replace(-,)}:EMPTY};}constafterCancelcancelWaitingJob(initialJobs,T-02);constviewbuildQueueView(afterCancel,8);// T-05 / 4 / 1 / NEXT_T05示例Map的意义是身份一致性检查而不是为了炫耀容器。真实项目中收到两条相同任务 ID 时不能默默以后者覆盖前者。这里只在构建视图时识别重复并返回DUPLICATE_ID而持久化、去重和入队确认仍是外围系统负责的事。页面被销毁时没有定时器或网络句柄需要释放如果未来每秒自动推动逻辑时钟应当在页面离开时停止前台定时器并将调度器放到独立生命周期中。配套 DevEco Studio 演示图中项目目录是FairExport/entry/src/main/ets/pages/ExportQueuePage.ets中间显示effectivePriority和dispatchOrder相关逻辑右侧模拟器给出队列排序底部 HiLog 的示例行是FQ-3412 tick8 total5 cancelled1 pending4 nextT-05 stateNEXT_T05。数字和代码可手算对应图中不包含虚构的文件系统读写结果也没有“导出成功”之类的断言。五、所谓公平是可解释的取舍不是绝对数学保证为什么老化阈值取三轮、分数封顶五档因为它们让这组数据的排序变化容易看见绝不表示这两个参数适用于全部离线队列。真实产品首先要回答什么叫“等待一轮”一次调度决策、一个固定时间窗还是一个执行完成事件。若任务耗时相差巨大只按决策次数增加权重可能仍然不公平若高优先级任务存在强时效承诺盲目老化又可能让低优先级任务抢走关键资源。纯手机演示图固定显示FQ-3412、tick8、原始任务五条、取消一条、待调度四条。队头T-05的有效分数为四后面的T-01、T-04、T-03都是三。一个容易忽略的视觉细节是被取消的T-02不应在“等待队列”中继续占一个名次但可以在底部审计提示中出现。这样使用者既不会误认为它还要执行也能找到撤销痕迹。六、验收时我会特意塞进三个坏数据第一组坏数据是时钟倒退例如入队轮次为九而当前轮次为八。上面的分数函数不会出现负等待但实际调度层应进一步拒绝不合规时钟来源。第二组是重复任务 ID示例直接进入DUPLICATE_ID禁止向“可执行下一项”的流程放行。第三组是全部取消队列排序应该返回空数组、下一任务 ID 是空字符串、状态为EMPTY页面必须显示“暂无待调度任务”而不是让旧卡片残留在列表顶部。还有一个边界更像架构问题两个页面都在调用“计算下一项”不等于两边可以同时启动任务。当前例子只做确定性候选选择没有并发领取协议。真的进入生产后需要执行器具备原子领取、失败恢复、持久化账本和取消确认否则一张正确的排序截图也无法证明不会重复导出同一份文件。最终这次练习并没有造出多么复杂的队列框架。它留下的是一条实用界线基础优先级属于任务事实老化分数属于某个时刻的派生判断取消记录属于审计事实。把三者拆开以后页面无需猜测任务的历史也能按相同输入重复画出同一份结果。本文所有界面和日志均为设计演示尚无目标 SDK 编译与真实导出吞吐测试。七、官方文档与验证边界ArkTS 官方简介https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-overview 其中介绍 ArkTS 的基本类型与容器能力。状态驱动与组件渲染可进一步阅读 ArkUI 概述https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui-overview 。本篇的老化打分、取消语义与排序规则为纯应用层代码不涉及华为官方提供的导出调度 API。示例应在目标 DevEco Studio、目标 API 版本下重新编译并针对真实业务补充持久化与并发验收。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑