TIME(延时和定时器)
# 简介
在LuatOS-Air脚本程序中,往往需要用到延时和等待等逻辑功能。例如,你想得到每隔30秒查询当前内存,这里就需要用到延时和定时器。在每次打印后加上一个30秒的定时器或者30秒的延迟,既可以实现这个功能。本篇文章将介绍2个延时函数和4个定时器函数以及一个判定定时器激活状态的函数,讲解这些函数如何使用以及在什么环境下使用。
# API说明
> 详细的API介绍见[sys API章节](https://doc.openluat.com/wiki/21?wiki_page_id=2295)和[rtos API章节](https://doc.openluat.com/wiki/21?wiki_page_id=2247)
# 实现流程
## 创建
sys.wait():
* sys.wait(ms):既属于延时也属于定时器,但只能适用于任务函数中(task)。它功能就是被调用时挂起当前协程,等延时时间结束结束后再继续执行剩下的协程功能。原理是先去启动一个定时器,将当前的协程挂起。如需在定时器计时结束前退出,可使用coroutine.resume接口重新激活协程。
创建也很简单,例子:sys.wait(1000) 会停止当前的协程任务,等1000ms后延时函数正常消亡,继续回来执行剩下脚本。
rtos.sleep():
* rtos.sleep(ms):属于底层延时函数,可用于任何环境。函数执行原理:执行该函数会将操作系统中的Lua虚拟机任务挂起,在计时结束前无 法退出。
创建示例:rtos.sleep(1000) 执行后,Lua虚拟机任务就会被挂起,1000ms后函数正常消亡,继续执行Lua虚拟机任务。
接下来是介绍定时器,共有四种定时器,他们各有各的特点。所以我们在使用时一定要记清楚这些定时器的特点,挑选适合当前程序需求的定时器使用。在LuatOS-Air脚本程序中,同时运行的定时器是有数量限制的。8910平台的LuatOS-Air最多支持50个定时器,如何理解50个定时器?在下面的知识扩展里详细讲解了。sys.timerStart()和function sys.timerLoopStart()的返回值是定时器ID,这个可以用来判断是否为同一个
定时器。或者也可以通过定时器的回调函数和回调参数是否相同来判断。
sys.timerStart():
* sys.timerStart(fnc,ms,...):属于定时器,单词定时器,适用场景不限定。第一个参数是回调函数,第二个是超时时间(ms),第三个是回调函数的参数,返回值是定时器ID。在创建前会查找当前开启的定时器,有相同定时器ID或者回调函数和回调参数相同时,就会关闭签一个定时器,再创建新的定时器,重新计时。
function sys.timerLoopStart():
* function sys.timerLoopStart(fnc,ms, ...):属定时器,循环定时器,适用场景不限定。用法特点同单此定时器几乎相同。唯一区别就是单此和多次循环。
sys.wait()(仅适用于task):
* function sys.wait(ms):延时介绍过,属于延时也属于定时器。单次定时器,适用场景仅限定task内。
sys.waitUntil()(仅适用于task):
* sys.waitUntil(id,ms):属于定时器,单次定时器,适用场景不限定。第一个参数是消息ID,第二个是超时间。sys.waitUntil()的特性与sys.timerStart()的完全一样,区别是sys.waitUntil()仅限于task内部使用。特性与详细介绍看下图。

## 消亡
### 自动消亡
1. sys.timerStart:收到定时器事件,执行回调函数后,lib中自动消亡
2. sys.timerLoopStart:收到定时器事件,每次执行回调函数后,lib中重新创建
3. sys.wait:挂起的task收到定时器事件后,lib中自动消亡
4. sys.waitUntil:挂起的task收到消息事件或者定时器事件后,lib中自动消亡
### 手动消亡
手动消亡有两种方式:sys.timerstop()和sys.timerstopAll()
1.sys.timerstop(val,...)
参数是定时器ID或者定时器的回调函数和回调参数。使用这个函数可以关闭指定的一个定时器。
返回值为nil

2.sys.timerstopAll(fnc)
功能:关闭sys.timerStart和sys.timerLoopStart创建的某一个回调函数的所有定时器
参数:需要关闭的定时器回调函数。
返回值:nil

已经消亡的定时器,再去执行stop不会产生异常
## 判断定时器状态
**sys.timerIsActive()**
功能:判断“通过timerStart或者timerStart创建的定时器”是否处于激活状态
参数:通过timerStart或者timerStart创建的定时器返回的定时器id或者回调函数与回调参数。
参数:
参数为回调函数与回调参数时,如果处于激活状态,则返回bool类型的true,否则返回nil。
返回值:
参数为定时器ID时,如果处于激活状态,则返回function类型的定时器回调函数,否则返回nil
例子:
```lua
local function timerCbFnc2(tag)
log.info("timerCbFnc2",tag)
end
sys.timerStart(timerCbFnc2,5000,"test")
sys.taskInit(function()
sys.wait(3000)
log.info("after 3 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
sys.wait(3000)
log.info("after 6 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
end)
```
结果:

-----
# 知识拓展
1.8910平台的LuatOS-Air最多支持50个定时器,如何理解50个定时器?
看下列两端代码,猜测,最后分别还有几个定时器在运行。
```lua
sys.taskInit(function()
for i=1,50 do
sys.timerStart(function() end,1000)
end
log.info("不考虑其它代码中定时器的使用")
log.info("运行到这里,同时存在多少个定时器?")
end)
```
```lua
sys.taskInit(function()
for i=1,50 do
sys.wait(1000)
end
log.info("不考虑其它代码中定时器的使用")
log.info("运行到这里,同时存在多少个定时器?")
end)
```
第一段代码是创建50个回调函数相同,延时时间相同的单此定时器。通过前面的介绍可知, 如果回调函数和回调参数都相同的两个sys.timerStart()定时器,可视为同一定时器。当第一个sys.timerStart(function() end,1000)创建好后,再创建第二个sys.timerStart(function() end,1000)时,就会查找当前存在的定时器里,是否有相同的定时器,如果就就会将第一个关闭,创建第二个相同的定时器,并重新开始计时。所以第一个代码段最后吃剩一个定时器在运行。
第二段代码是创建50个sys.wait(1000)。当创建第一个sys.wait(1000)后,这个协程就会被挂起。1000ms后,继续执行剩下的程序,创建下一个sys.wait(1000)。所以,最后也只剩一个定时器。
下面这段代码,通过改变回调参数来同时创建50个sys.timerLoopStart(log.info,1000,"nCounter1",i)。但看打印结果会发现,没到50个。因为打印出来的数量只是sys.timerLoopStart(log.info,1000,"nCounter1",i)的数量,还有sys.wait(5000)等一些定时器没算在内。
```lua
local function wait1()
sys.wait(5000)
for i=1,50 do
sys.timerLoopStart(log.info,1000,"nCounter1",i)
end
end
sys.taskInit(wait1)
```
# 示例
> 详细的API demo见[The Timer demo章节](https://doc.openluat.com/wiki/21?wiki_page_id=2311)
1.waitUntil()、wait()
* waitUntil()和wait()sys.wait和sys.waitUntil只能用在task中,或者被task的主函数直接或者间接调用。
```lua
local function IndirectCall()
sys.waitUntil("MSG_ID",5000)
end
sys.taskInit(function()
sys.wait(5000)
IndirectCall()
log.info("sys.wait和sys.waitUntil只能用在task中")
log.info("只能被task的主函数直接或者间接调用")
end)
```
2.timerStart()
* timerStart()是单此定时器
```lua
local function publicTimerCbFnc(tag)
log.info("publicTimerCbFnc",tag)
end
sys.timerStart(publicTimerCbFnc,8000,"first")
local timerId2 = sys.timerStart(publicTimerCbFnc,8000,"second")
sys.timerStart(publicTimerCbFnc,8000,"third")
```
3.timerLoopStart()
* timerLoopStart()是循环定时器
```lua
local nCounter = 0
sys.taskInit(function ()
while true do
nCounter = nCounter+1
sys.wait(1000)
end
end)
sys.timerLoopStart(log.info,1000,"nCounter1",nCounter)
--sys.timerLoopStart(log.info,1000,"nCounter1",0)
sys.timerLoopStart(function() log.info("nCounter2",nCounter) end,1000)0,"first")
```
# 常见问题
## 1,定时器精度问题
LuatOS-Air开发最小仅支持5毫秒的定时器,超时时间最小1ms(实际支持的最小时间是5ms,小于5ms的时间都被转化为5ms) 超时间最大0x7FFFFFFF(24.85天)。
另外毫秒级的定时器的误差较大,原因可参考[LuatOS-Air应用脚本运行框架](https://doc.openluat.com/wiki/21?wiki_page_id=1926)
## 2,一个项目中sys.timerLoopStart定时器,能开几个?有没有限制?
LuatOS-Air最多支持50个定时器,详细说明看上面 [知识拓展](https://hmi.wiki.luatos.com/doc/65042949/e6zPC3k9/MgfM7VP0#nav_9 "知识拓展")
## 3,sys.timerLoopStart定时器的值,可以用变量动态改动吗?
不可以,第一次调用的时候会把值传进去,之后会用第一次传的值,不会随着变量值的变化而改变,测试效果如下:
```lua
local testm = 1000
local function test()
print("test testm",testm)
testm = testm + 1000
end
sys.timerLoopStart(test,testm)
```
