零、JSyncQueue
JSyncQueue 是一个开箱即用的鸿蒙异步任务同步队列。
一、JSyncQueue 有什么作用
在鸿蒙应用开发中,有时需要让多个异步任务按顺序执行,例如状态的转换处理,如果不加控制,会因为执行顺序混乱而产生一些莫名其妙的问题。 所以 JSyncQueue 提供了一个简洁的解决方案:
- 保证顺序执行:所有任务严格按照入队顺序执行,即使任务内部有异步操作也能保证顺序
- 两种执行模式:支持 "立即执行" 和 "延时执行" 两种模式,可以满足不同场景需求
- 两种任务类型:支持向同步队列添加 "Message 类型任务" 和 "Runnable 类型任务"
- 任务取消和管理:可随时取消指定任务或清空整个队列
- 获取任务结果:通过任务的
getResult()获取执行结果
项目架构如下图所示:
二、如何安装 JSyncQueue
第一种方式: 在需要使用 JSyncQueue 的模块中运行以下命令
1ohpm install jsyncqueue 2
第二种方式: 在需要使用 JSyncQueue 的模块 oh-package.json5 中添加以下依赖
1{ 2 "name": "sample", 3 "version": "1.0.0", 4 "description": "Please describe the basic information.", 5 "main": "", 6 "author": "", 7 "license": "", 8 "dependencies": { 9 "jsyncqueue": "1.0.0" // 添加这一行,请根据需要修改版本号 10 } 11} 12
三、JSyncQueue API 介绍
3-1、JSyncQueue 类
构造函数
1constructor(queueName: string) 2
创建一个同步队列实例。
queueName: 队列名称,用于标识和调试
方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| post(runnable) | runnable: (taskId: number) => Promise<Any> | Task | 立即执行闭包 |
| postDelay(runnable, delay) | runnable: (taskId: number) => Promise<Any>, delay: number | Task | 延时 delay 毫秒执行闭包 |
| sendMessage(message) | message: Message | Task | 立即发送消息 |
| sendMessageDelay(message, delay) | message: Message, delay: number | Task | 延时 delay 毫秒发送消息 |
| cancel(taskId) | taskId: number | void | 取消指定任务 |
| clear() | - | void | 清空队列中所有等待的任务 |
| dumpInfo() | - | string | 获取队列调试信息 |
| onHandleMessage(message, taskId) | message: Message, taskId: number | Promise<Any> | 消息处理方法,子类可重写 |
属性
| 属性 | 类型 | 说明 |
|---|---|---|
| queueName | string | 队列名称(只读) |
| length | number | 当前队列中的任务数量(只读) |
3-2、Message 接口
1interface Message { 2 what: string // 消息类型 3 data: Any // 消息数据 4} 5
3-3、Task 接口
所有添加的任务,包括“Message 类型任务”和“Runnable 类型任务”,均会返回该类型实例,通过该实例可以“取消任务”、“获取任务结果”、“任务 Id”。
1interface Task { 2 cancel(): void // 取消任务 3 getResult(): Promise<Any> // 获取任务结果 4 getTaskId(): number // 获取任务 ID 5} 6
3-4、异常类型
JSyncQueueCancelException
当任务被取消时,会抛出该类型的异常。
1interface JSyncQueueCancelException { 2 message: string 3} 4
JSyncQueueException
当 JSyncQueue 内部发生异常时,会抛出该类型的异常。
值得注意:使用者编写的逻辑中抛出的异常会原封不动的抛到
Task.getResult().catch中,而不是以JSyncQueueException类型抛出。
1interface JSyncQueueException { 2 message: string 3} 4
四、如何使用 JSyncQueue
4-1、使用 JSyncQueue 创建同步队列
如果你处理的场景均是简单的一次性任务,那么直接使用 JSyncQueue 创建一个同步队列,并压入 Runnable 闭包即可。
以下代码展示的逻辑细节:
- 代码中使用了 delay 函数模拟了两次耗时操作,并且返回结果
- 外部通过
Task类型实例接收返回结果,并且打印 - 在第四次循环(即 i 为 3)的时候,会模拟抛出异常,异常内容会原封不动的抛到
catch中
值得注意:
- 立即执行任务会严格按入队顺序执行
- 任务结果的接收处理(即对
Task.getResult()的处理)和JSyncQueue对任务的处理是不保证顺序的,因为Task.getResult()的处理已不在队列范围内
1immediatelyJSyncQueue: JSyncQueue = new JSyncQueue("ImmediatelyJSyncQueue") 2for (let i = 0; i < 5; ++i) { 3 const task = this.immediatelyJSyncQueue.post(async () => { 4 const delayTime1 = Math.round(Math.random() * 500) 5 Log.i(TAG, [`【添加5个Runnable】执行逻辑 i=${i} 第一段 将会模拟耗时=${delayTime1}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 6 await this.delay(delayTime1) 7 8 if (i == 3) { 9 throw { message: "模拟异常" } as Error 10 } 11 12 const delayTime2 = Math.round(Math.random() * 500) 13 Log.i(TAG, [`【添加5个Runnable】执行逻辑 i=${i} 第二段 将会模拟耗时=${delayTime2}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 14 await this.delay(delayTime2) 15 16 return [`jiangpengyong-添加5个Runnable ${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md) 17 }) 18 task.getResult() 19 .then((result) => { 20 Log.i(TAG, [`【添加5个Runnable-执行成功】i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 21 }) 22 .catch((e: Error) => { 23 Log.e(TAG, [`【添加5个Runnable-执行异常】i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 24 }) 25 .finally(() => { 26 Log.i(TAG, [`【添加5个Runnable-执行结束】i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 27 }) 28} 29 30// ========================================= 输出日志 ========================================= 31// 【添加5个Runnable】执行逻辑 i=0 第一段 将会模拟耗时=239 32// 【添加5个Runnable】执行逻辑 i=0 第二段 将会模拟耗时=315 33// 【添加5个Runnable】执行逻辑 i=1 第一段 将会模拟耗时=379 34// 【添加5个Runnable-执行成功】i=0 result=jiangpengyong-添加5个Runnable 0 35// 【添加5个Runnable-执行结束】i=0 36// 【添加5个Runnable】执行逻辑 i=1 第二段 将会模拟耗时=391 37// 【添加5个Runnable】执行逻辑 i=2 第一段 将会模拟耗时=499 38// 【添加5个Runnable-执行成功】i=1 result=jiangpengyong-添加5个Runnable 1 39// 【添加5个Runnable-执行结束】i=1 40// 【添加5个Runnable】执行逻辑 i=2 第二段 将会模拟耗时=395 41// 【添加5个Runnable】执行逻辑 i=3 第一段 将会模拟耗时=478 42// 【添加5个Runnable-执行成功】i=2 result=jiangpengyong-添加5个Runnable 2 43// 【添加5个Runnable-执行结束】i=2 44// 【添加5个Runnable】执行逻辑 i=4 第一段 将会模拟耗时=166 45// 【添加5个Runnable-执行异常】i=3 e={"message":"模拟异常"} 46// 【添加5个Runnable-执行结束】i=3 47// 【添加5个Runnable】执行逻辑 i=4 第二段 将会模拟耗时=33 48// 【添加5个Runnable-执行成功】i=4 result=jiangpengyong-添加5个Runnable 4 49// 【添加5个Runnable-执行结束】i=4 50
取消同步任务
通过返回的 Task 类型实例调用 cancel 方法可以进行取消任务。
下面的代码会取消第四次任务,所以在日志中会看到对应的取消异常,并且不会执行该任务。
1let task: Task | undefined 2for (let i = 0; i < 5; ++i) { 3 const tempTask = this.immediatelyJSyncQueue.post(async () => { 4 const delayTime1 = Math.round(Math.random() * 500) 5 Log.i(TAG, [`【移除Runnable】执行逻辑 i=${i} 第一段 将会模拟耗时=${delayTime1}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 6 await this.delay(delayTime1) 7 8 const delayTime2 = Math.round(Math.random() * 500) 9 Log.i(TAG, [`【移除Runnable】执行逻辑 i=${i} 第二段 将会模拟耗时=${delayTime2}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 10 await this.delay(delayTime2) 11 12 if (i == 2) { 13 throw { message: "模拟异常" } as Error 14 } 15 return [`jiangpengyong-移除Runnable ${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md) 16 }) 17 tempTask.getResult().then((result) => { 18 Log.i(TAG, [`【移除Runnable】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 19 }).catch((e: Any) => { 20 Log.e(TAG, [`【移除Runnable】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 21 }).finally(() => { 22 Log.i(TAG, [`【移除Runnable】执行完成 i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 23 }) 24 if (i == 3) { 25 task = tempTask 26 } 27} 28Log.i(TAG, `【移除Runnable】取消任务 task=${JSON.stringify(task)}`) 29task?.cancel() 30 31// ========================================= 输出日志 ========================================= 32// 【移除Runnable】执行逻辑 i=0 第一段 将会模拟耗时=263 33// 【移除Runnable】取消任务 task={"taskId":13,"queue":{},"promise":{}} 34// 【移除Runnable】执行异常 i=3 e={"message":"Cancel task by cancel function."} 35// 【移除Runnable】执行完成 i=3 36// 【移除Runnable】执行逻辑 i=0 第二段 将会模拟耗时=474 37// 【移除Runnable】执行逻辑 i=1 第一段 将会模拟耗时=318 38// 【移除Runnable】执行成功 i=0 result=jiangpengyong-移除Runnable 0 39// 【移除Runnable】执行完成 i=0 40// 【移除Runnable】执行逻辑 i=1 第二段 将会模拟耗时=6 41// 【移除Runnable】执行逻辑 i=2 第一段 将会模拟耗时=406 42// 【移除Runnable】执行成功 i=1 result=jiangpengyong-移除Runnable 1 43// 【移除Runnable】执行完成 i=1 44// 【移除Runnable】执行逻辑 i=2 第二段 将会模拟耗时=212 45// 【移除Runnable】执行逻辑 i=4 第一段 将会模拟耗时=226 46// 【移除Runnable】执行异常 i=2 e={"message":"模拟异常"} 47// 【移除Runnable】执行完成 i=2 48// 【移除Runnable】执行逻辑 i=4 第二段 将会模拟耗时=439 49// 【移除Runnable】执行成功 i=4 result=jiangpengyong-移除Runnable 4 50// 【移除Runnable】执行完成 i=4 51
延时执行 Runnable 类型任务
添加延时任务只需改用 postDelay 方法并传入延时参数
- 下面代码记录了添加任务到真正执行的延时,通过
realDelay参数可以查看 - 使用了
delay函数模拟了两次耗时操作,并模拟返回了处理结果 - 第四次任务抛出了异常,异常消息会原封不动的在
catch的日志展示 - 因为延时任务的添加是按索引进行累加的,所以添加顺序其实并没变化,从最后的日志输出可以看到保证了执行顺序
1for (let i = 0; i < 5; ++i) { 2 const startTime = systemDateTime.getTime(false) 3 const delayTime = i * 100 4 const task = this.delayJSyncQueue.postDelay(async () => { 5 const endTime = systemDateTime.getTime(false) 6 const realDelay = endTime - startTime 7 const delayTime1 = Math.round(Math.random() * 500) 8 Log.i(TAG, [`【添加5个Runnable】执行逻辑 delay=${delayTime} realDelay=${realDelay} i=${i} 第一段 将会模拟耗时=${delayTime1}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 9 await this.delay(delayTime1) 10 11 const delayTime2 = Math.round(Math.random() * 500) 12 Log.i(TAG, [`【添加5个Runnable】执行逻辑 i=${i} 第二段 将会模拟耗时=${delayTime2}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 13 await this.delay(delayTime2) 14 15 if (i == 3) { 16 throw { message: "模拟异常" } as Error 17 } 18 return [`jiangpengyong-添加5个Runnable ${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md) 19 }, delayTime) 20 task.getResult() 21 .then((result) => { 22 Log.i(TAG, [`【添加5个Runnable】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 23 }) 24 .catch((e: Error) => { 25 Log.e(TAG, [`【添加5个Runnable】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 26 }) 27 .finally(() => { 28 Log.i(TAG, [`【添加5个Runnable】执行结束 i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 29 }) 30} 31 32// ========================================= 输出日志 ========================================= 33// 【添加5个Runnable】执行逻辑 delay=0 realDelay=1 i=0 第一段 将会模拟耗时=473 34// 【添加5个Runnable】执行逻辑 i=0 第二段 将会模拟耗时=410 35// 【添加5个Runnable】执行逻辑 delay=100 realDelay=888 i=1 第一段 将会模拟耗时=178 36// 【添加5个Runnable】执行成功 i=0 result=jiangpengyong-添加5个Runnable 0 37// 【添加5个Runnable】执行结束 i=0 38// 【添加5个Runnable】执行逻辑 i=1 第二段 将会模拟耗时=204 39// 【添加5个Runnable】执行逻辑 delay=200 realDelay=1272 i=2 第一段 将会模拟耗时=410 40// 【添加5个Runnable】执行成功 i=1 result=jiangpengyong-添加5个Runnable 1 41// 【添加5个Runnable】执行结束 i=1 42// 【添加5个Runnable】执行逻辑 i=2 第二段 将会模拟耗时=36 43// 【添加5个Runnable】执行逻辑 delay=300 realDelay=1721 i=3 第一段 将会模拟耗时=475 44// 【添加5个Runnable】执行成功 i=2 result=jiangpengyong-添加5个Runnable 2 45// 【添加5个Runnable】执行结束 i=2 46// 【添加5个Runnable】执行逻辑 i=3 第二段 将会模拟耗时=483 47// 【添加5个Runnable】执行逻辑 delay=400 realDelay=2686 i=4 第一段 将会模拟耗时=9 48// 【添加5个Runnable】执行异常 i=3 e={"message":"模拟异常"} 49// 【添加5个Runnable】执行结束 i=3 50// 【添加5个Runnable】执行逻辑 i=4 第二段 将会模拟耗时=395 51// 【添加5个Runnable】执行成功 i=4 result=jiangpengyong-添加5个Runnable 4 52// 【添加5个Runnable】执行结束 i=4 53
取消延时任务
延时任务的取消操作和立即执行的取消操作是完全一样的,都是通过返回的 Task 实例调用 cancel 方法,这里就不再赘述。
4-2、继承 JSyncQueue 创建同步队列
如果你的同步逻辑需要集中管理或进行复用,可以考虑 Message 类型任务。
处理 Message 类型任务,需要继承 JSyncQueue 实现 onHandleMessage 方法,在该方法中会按入队顺序接收到 Message :
- 通过
Message.what属性区分不同类别消息实现不同处理逻辑 - 通过
Message.data属性可以获取外部传入的数据,数据类型是Any可以是任意类型数据,使用者自行转换为真实类型进行逻辑处理
具体操作如下:
- 定义一个
ImmediatelyQueue类继承JSyncQueue,实现onHandleMessage方法 - 创建一个
ImmediatelyQueue实例,并通过这个实例进行发送 Message 消息,同步队列会按入队顺序一个个进行分发给该实例的onHandleMessage方法进行处理
1// 自定义 JSyncQueue 2export class ImmediatelyQueue extends JSyncQueue { 3 private count = 0 4 5 async onHandleMessage(message: Message): Promise<Any> { 6 switch (message.what) { 7 case "say_hello": { 8 const name = message.data["name"] 9 this.count += 1 10 11 const delayTime1 = Math.round(Math.random() * 500) 12 Log.i("ImmediatelyQueue", `【say_hello】执行逻辑 第一段 将会模拟耗时=${delayTime1}`) 13 await this.delay(delayTime1) 14 15 const delayTime2 = Math.round(Math.random() * 500) 16 Log.i("ImmediatelyQueue", `【say_hello】执行逻辑 第二段 将会模拟耗时=${delayTime2}`) 17 await this.delay(delayTime2) 18 19 if (this.count % 10 == 5) { 20 throw { message: "模拟异常" } 21 } 22 return `你好,${name}。这是第${this.count}次打招呼。` 23 } 24 // ... 其他 what 处理逻辑 25 } 26 return undefined 27 } 28 29 private async delay(ms: number) { 30 return new Promise<Any>(resolve => setTimeout(resolve, ms)) 31 } 32} 33 34// 使用逻辑 35immediatelyQueue: JSyncQueue = new ImmediatelyQueue("ImmediatelyQueue") 36for (let i = 0; i < 5; ++i) { 37 const tempTask = this.immediatelyQueue.sendMessage({ 38 what: `say_hello`, 39 data: { name: '江澎涌', age: 20 + i }, 40 }) 41 tempTask.getResult() 42 .then((result) => { 43 Log.i(TAG, [`【添加5个Message】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 44 }) 45 .catch((e: Error) => { 46 Log.e(TAG, [`【添加5个Message】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 47 }) 48 .finally(() => { 49 Log.i(TAG, [`【添加5个Message】执行结束i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 50 }) 51} 52// ========================================= 输出日志 ========================================= 53// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":20}} 54// 【say_hello】执行逻辑 第一段 将会模拟耗时=92 55// 【say_hello】执行逻辑 第二段 将会模拟耗时=143 56// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":21}} 57// 【say_hello】执行逻辑 第一段 将会模拟耗时=276 58// 【say_hello】执行逻辑 第二段 将会模拟耗时=377 59// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":22}} 60// 【say_hello】执行逻辑 第一段 将会模拟耗时=120 61// 【say_hello】执行逻辑 第二段 将会模拟耗时=223 62// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":23}} 63// 【say_hello】执行逻辑 第一段 将会模拟耗时=424 64// 【say_hello】执行逻辑 第二段 将会模拟耗时=444 65// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":24}} 66// 【say_hello】执行逻辑 第一段 将会模拟耗时=181 67// 【say_hello】执行逻辑 第二段 将会模拟耗时=402 68
移除 Message 消息
使用 sendMessage 方法压入 “Message 类型任务” 同样会返回 Task 类型实例,调用该实例的 cancel 方法就可以取消该任务。
下列代码会取消第二个任务,所以不会看到 "age":11 的消息。
1let task: Task | undefined 2for (let i = 0; i < 5; ++i) { 3 const tempTask = this.immediatelyQueue.sendMessage({ 4 what: `remove_message`, 5 data: { name: 'jiang peng yong', age: 10 + i }, 6 }) 7 tempTask.getResult().then((result) => { 8 Log.i(TAG, [`【移除Message】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 9 }).catch((e: Any) => { 10 Log.e(TAG, [`【移除Message】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 11 }).finally(() => { 12 Log.i(TAG, [`【移除Message】执行完成 i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 13 }) 14 if (i == 1) { 15 task = tempTask 16 } 17} 18Log.i(TAG, `【移除Message】取消任务 task=${JSON.stringify(task)}`) 19task?.cancel() 20// ========================================= 输出日志 ========================================= 21// onHandleMessage message={"what":"remove_message","data":{"name":"jiang peng yong","age":10}} 22// 【remove_message】执行逻辑 第一段 将会模拟耗时=497 23// 【remove_message】执行逻辑 第二段 将会模拟耗时=397 24// onHandleMessage message={"what":"remove_message","data":{"name":"jiang peng yong","age":12}} 25// 【remove_message】执行逻辑 第一段 将会模拟耗时=162 26// 【remove_message】执行逻辑 第二段 将会模拟耗时=283 27// onHandleMessage message={"what":"remove_message","data":{"name":"jiang peng yong","age":13}} 28// 【remove_message】执行逻辑 第一段 将会模拟耗时=193 29// 【remove_message】执行逻辑 第二段 将会模拟耗时=93 30// onHandleMessage message={"what":"remove_message","data":{"name":"jiang peng yong","age":14}} 31// 【remove_message】执行逻辑 第一段 将会模拟耗时=359 32// 【remove_message】执行逻辑 第二段 将会模拟耗时=145 33
延时执行 Message 类型任务
- 定义一个
DelayQueue类继承JSyncQueue,主要重写onHandleMessage方法,用于接收处理Message - 创建
DelayQueue实例,通过这个实例调用sendMessageDelay方法即可达到相应的延时效果
1export class DelayQueue extends JSyncQueue { 2 private count = 0 3 4 async onHandleMessage(message: Message): Promise<Any> { 5 Log.i("DelayQueue", `onHandleMessage message=${JSON.stringify(message)}`) 6 switch (message.what) { 7 case "say_hello": { 8 const name = message.data["name"] 9 this.count += 1 10 11 const delayTime1 = Math.round(Math.random() * 500) 12 Log.i("DelayQueue", `【say_hello】执行逻辑 第一段 将会模拟耗时=${delayTime1}`) 13 await this.delay(delayTime1) 14 15 const delayTime2 = Math.round(Math.random() * 500) 16 Log.i("DelayQueue", `【say_hello】执行逻辑 第二段 将会模拟耗时=${delayTime2}`) 17 await this.delay(delayTime2) 18 19 if (this.count % 10 == 5) { 20 throw { message: "模拟异常" } 21 } 22 return [`Hello,${name}. This is the ${this.count} th greeting.`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.th.md) 23 } 24 } 25 return undefined 26 } 27 28 private async delay(ms: number) { 29 return new Promise<Any>(resolve => setTimeout(resolve, ms)) 30 } 31} 32 33delayQueue: JSyncQueue = new DelayQueue("DelayQueue") 34for (let i = 0; i < 5; ++i) { 35 const delayTime = i * 100 36 const task = this.delayQueue.sendMessageDelay({ 37 what: `say_hello`, 38 data: { name: '江澎涌', age: 20 + i }, 39 }, delayTime) 40 task.getResult() 41 .then((result) => { 42 Log.i(TAG, [`【添加5个Message】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 43 }) 44 .catch((e: Error) => { 45 Log.e(TAG, [`【添加5个Message】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 46 }) 47 .finally(() => { 48 Log.i(TAG, [`【添加5个Message】执行结束i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 49 }) 50} 51// ========================================= 输出日志 ========================================= 52// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":20}} 53// 【say_hello】执行逻辑 第一段 将会模拟耗时=356 54// 【say_hello】执行逻辑 第二段 将会模拟耗时=302 55// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":21}} 56// 【say_hello】执行逻辑 第一段 将会模拟耗时=67 57// 【say_hello】执行逻辑 第二段 将会模拟耗时=344 58// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":22}} 59// 【say_hello】执行逻辑 第一段 将会模拟耗时=339 60// 【say_hello】执行逻辑 第二段 将会模拟耗时=384 61// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":23}} 62// 【say_hello】执行逻辑 第一段 将会模拟耗时=442 63// 【say_hello】执行逻辑 第二段 将会模拟耗时=392 64// onHandleMessage message={"what":"say_hello","data":{"name":"江澎涌","age":24}} 65// 【say_hello】执行逻辑 第一段 将会模拟耗时=443 66// 【say_hello】执行逻辑 第二段 将会模拟耗时=199 67
取消延时的 Message 类型任务
延时任务的取消操作和立即执行的取消操作是完全一样的,都是通过返回的 Task 实例调用 cancel 方法,这里就不再赘述。
同一队列压入 Message 类型任务和 Runnable 类型任务
对 JSyncQueue 同一实例压入 Message 和 Runnable 两种类型任务是支持的,会按照压入顺序进行执行和分发。
1// ImmediatelyQueue 源码就不再展示,需要可以移步 Github 上查阅 2immediatelyQueue: JSyncQueue = new ImmediatelyQueue("ImmediatelyQueue") 3for (let i = 0; i < 10; ++i) { 4 if (i % 2 == 0) { 5 this.immediatelyQueue.post(async () => { 6 const delayTime1 = Math.round(Math.random() * 500) 7 Log.i(TAG, [`【添加10个Message和Runnable】执行逻辑 i=${i} 第一段 将会模拟耗时=${delayTime1}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 8 await this.delay(delayTime1) 9 10 const delayTime2 = Math.round(Math.random() * 500) 11 Log.i(TAG, [`【添加10个Message和Runnable】执行逻辑 i=${i} 第二段 将会模拟耗时=${delayTime2}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 12 await this.delay(delayTime2) 13 14 if (i / 2 == 3) { 15 throw { message: "模拟异常" } as Error 16 } 17 return [`小朋友-添加10个Message和Runnable ${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md) 18 }) 19 } else { 20 this.immediatelyQueue.sendMessage({ 21 what: `say_hello`, 22 data: { name: '小朋友', age: i }, 23 }) 24 } 25} 26// ========================================= 输出日志 ========================================= 27// 【添加10个Message和Runnable】执行逻辑 i=0 第一段 将会模拟耗时=416 28// 【添加10个Message和Runnable】执行逻辑 i=0 第二段 将会模拟耗时=41 29// onHandleMessage message={"what":"say_hello","data":{"name":"小朋友","age":1}} 30// 【say_hello】执行逻辑 第一段 将会模拟耗时=184 31// 【say_hello】执行逻辑 第二段 将会模拟耗时=63 32// 【添加10个Message和Runnable】执行逻辑 i=2 第一段 将会模拟耗时=451 33// 【添加10个Message和Runnable】执行逻辑 i=2 第二段 将会模拟耗时=223 34// onHandleMessage message={"what":"say_hello","data":{"name":"小朋友","age":3}} 35// 【say_hello】执行逻辑 第一段 将会模拟耗时=99 36// 【say_hello】执行逻辑 第二段 将会模拟耗时=27 37// 【添加10个Message和Runnable】执行逻辑 i=4 第一段 将会模拟耗时=273 38// 【添加10个Message和Runnable】执行逻辑 i=4 第二段 将会模拟耗时=193 39// onHandleMessage message={"what":"say_hello","data":{"name":"小朋友","age":5}} 40// 【say_hello】执行逻辑 第一段 将会模拟耗时=20 41// 【say_hello】执行逻辑 第二段 将会模拟耗时=231 42// 【添加10个Message和Runnable】执行逻辑 i=6 第一段 将会模拟耗时=46 43// 【添加10个Message和Runnable】执行逻辑 i=6 第二段 将会模拟耗时=198 44// onHandleMessage message={"what":"say_hello","data":{"name":"小朋友","age":7}} 45// 【say_hello】执行逻辑 第一段 将会模拟耗时=179 46// 【say_hello】执行逻辑 第二段 将会模拟耗时=0 47// 【添加10个Message和Runnable】执行逻辑 i=8 第一段 将会模拟耗时=131 48// 【添加10个Message和Runnable】执行逻辑 i=8 第二段 将会模拟耗时=401 49// onHandleMessage message={"what":"say_hello","data":{"name":"小朋友","age":9}} 50// 【say_hello】执行逻辑 第一段 将会模拟耗时=452 51// 【say_hello】执行逻辑 第二段 将会模拟耗时=40 52
4-3、取消队列中所有任务
对 JSyncQueue 实例调用 clear 方法,就会把队列中等待执行的任务,包括延时执行和立即执行的任务,全都取消。同时会抛出 JSyncQueueCancelException 类型异常。
1for (let i = 0; i < 5; ++i) { 2 const task = this.immediatelyQueue.post(async () => { 3 const delayTime1 = Math.round(Math.random() * 500) 4 Log.i(TAG, [`【清空队列】执行逻辑 i=${i} 第一段 将会模拟耗时=${delayTime1}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 5 await this.delay(delayTime1) 6 7 const delayTime2 = Math.round(Math.random() * 500) 8 Log.i(TAG, [`【清空队列】执行逻辑 i=${i} 第二段 将会模拟耗时=${delayTime2}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 9 await this.delay(delayTime2) 10 11 return [`小朋友-清空队列 ${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md) 12 }) 13 task.getResult() 14 .then((result) => { 15 Log.i(TAG, [`【清空队列】执行成功 i=${i} result=${result}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 16 }) 17 .catch((e: Error) => { 18 Log.e(TAG, [`【清空队列】执行异常 i=${i} e=${JSON.stringify(e)}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 19 }) 20 .finally(() => { 21 Log.i(TAG, [`【清空队列】执行结束 i=${i}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.i.md)) 22 }) 23} 24this.immediatelyQueue.clear() 25// ========================================= 输出日志 ========================================= 26// 【清空队列】执行逻辑 i=0 第一段 将会模拟耗时=14 27// 【清空队列】执行异常 i=1 e={"message":"Cancel task by clear function."} 28// 【清空队列】执行异常 i=2 e={"message":"Cancel task by clear function."} 29// 【清空队列】执行异常 i=3 e={"message":"Cancel task by clear function."} 30// 【清空队列】执行异常 i=4 e={"message":"Cancel task by clear function."} 31// 【清空队列】执行结束 i=1 32// 【清空队列】执行结束 i=2 33// 【清空队列】执行结束 i=3 34// 【清空队列】执行结束 i=4 35// 【清空队列】执行逻辑 i=0 第二段 将会模拟耗时=125 36// 【清空队列】执行成功 i=0 result=小朋友-清空队列 0 37// 【清空队列】执行结束 i=0 38
五、作者博客
csdn:blog.csdn.net/weixin_3762…
公众号:微信搜索 "江澎涌"
《JSyncQueue——一个开箱即用的鸿蒙异步任务同步队列》 是转载文章,点击查看原文。
