定时器是研发过程中常用的 API,用于实现不希望代码立即运行,而是等待一段时间后再执行。例如在游戏开发中,可以使用定时器来控制游戏角色的动画播放速度和行动间隔;在数据同步场景中,可利用定时器定期检查并同步本地数据与服务器数据;在界面更新场景中,通过定时器定时刷新界面上的动态信息,如倒计时显示等。
但是,有一种情况下,会block定时器的调度,那就是在同一个函数中,启动了定时器后,在同一个线程执行了一个比较耗时的任务,这时该定时器还没有开始进行调度,会产生block,导致定时的操作没有按预期执行。
本篇内容是《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》这本书第9章内容的延续,是咱这本书读者的福利,在本篇内容中介绍定时器的基本写法及如书写不被block的定时器,欢迎大家一同来深入的了解,甚至可以当作面试题来学习。
对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)
《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利博文目录,点击查看
1.定时器的基本写法
下面的示例实现了基本的定时器的写法,100毫秒后执行
console.log("俩毛豆 测试定时器 start time");
setTimeout(()=>{
console.log("俩毛豆 测试定时器 time out");
},100)
console.log("俩毛豆 测试定时器 调用完成");
上面的代码,执行日志如下图所示,可以看出,定时器调用完成的日志先输出,之后约100毫秒定时器时间到。

2.增加长时任务
下面的示例在同一函数且同一线程内,调用定时器之后,增加一耗时1秒的任务。
function longRunningFun() {
console.log("俩毛豆 测试定时器 模拟耗时1秒开始");
const startTime = new Date().getTime();
while (new Date().getTime() - startTime < 1000) {
// 空循环,模拟1秒耗时
}
console.log("俩毛豆 测试定时器 模拟耗时1秒结束");
}
console.log("俩毛豆 测试定时器 start time");
setTimeout(()=>{
console.log("俩毛豆 测试定时器 time out");
},100)
longRunningFun();
console.log("俩毛豆 测试定时器 调用完成");
可以看出,耗时1秒的操作,影响了定时器的执行。

3.耗时操作使用异步的方式执行
下面的示例,将耗时的操作,使用异步的方式执行
function longRunningFun() {
console.log("俩毛豆 测试定时器 模拟耗时1秒开始");
const startTime = new Date().getTime();
while (new Date().getTime() - startTime < 1000) {
// 空循环,模拟1秒耗时
}
console.log("俩毛豆 测试定时器 模拟耗时1秒结束");
}
async function dolongRunningFunTask(): Promise<string> {
return new Promise(resolve => {
longRunningFun();
resolve("")
})
}
console.log("俩毛豆 测试定时器 start time");
setTimeout(()=>{
console.log("俩毛豆 测试定时器 time out");
},100)
dolongRunningFunTask().then(()=>{
console.log("俩毛豆 测试定时器 耗时任务执行完成 调用方收到事件");
});
console.log("俩毛豆 测试定时器 调用完成");
执行代码log如下图所示,可以看出,因为使用异步执行了耗时操作,定时器的超时时间,会在耗时操作之后(因为在同一个线程,且先被执行),但会在耗时操作执行完成后,立即执行(耗时操作1秒,定时器100毫秒)。

4.解决耗时操作对定时器的影响
从上面的示例代码可以看出,同一线程内的耗时操作会影响定时器的超时事件产生的时间的。如需要解决该问题,需要使用多线程的方式来执行耗时任务。
@Concurrent
function longRunningFunConcurrent() {
console.log("俩毛豆 测试定时器 Concurrent 模拟耗时1秒开始");
const startTime = new Date().getTime();
while (new Date().getTime() - startTime < 1000) {
// 空循环,模拟1秒耗时
}
console.log("俩毛豆 测试定时器 Concurrent 模拟耗时1秒结束");
}
async function dolongRunningFunConcurrentTask(): Promise<string> {
return new Promise(resolve => {
const task: taskpool.Task = new taskpool.Task(longRunningFunConcurrent);
taskpool.execute(task).then(() => {
resolve("")
})
})
}
console.log("俩毛豆 测试定时器 start time");
setTimeout(()=>{
console.log("俩毛豆 测试定时器 time out");
},100)
dolongRunningFunConcurrentTask().then(()=>{
console.log("俩毛豆 测试定时器 执行完成 调用方收到事件");
});
console.log("俩毛豆 测试定时器 调用完成");
执行log如下图所示,可以看出,定时器超时的事件,并没有被耗时操作影响

5.总结
本文探讨了定时器在鸿蒙应用开发中的使用注意事项。通过示例代码展示了定时器的基本写法,以及同一线程内耗时操作对定时器执行的阻塞影响。文章提出了两种解决方案:一是将耗时操作改为异步执行,二是使用多线程技术(如taskpool)来执行耗时任务。实验结果表明,多线程方式能有效避免耗时操作对定时器的影响。本文内容节选自《精通HarmonyOSNEXT:鸿蒙App开发入门与项目化实战》一书,为读者提供了实用的定时器优化技巧。
对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)