这里分享的内容使用的是 js 语言,在其它语言也具有一样的原理。
在实现定时执行某个任务时,有两种实现思路。
1.
hInterval = setInterval(function(){ task() }, 1000)
// 如欲停止执行:
clearInterval(hInterval)
2.
hTimeout = setTimeout(function f(){ task() hTimeout = setTimeout(f, 1000) // 通过延时递归实现反复执行 }, 100)
这两种做法严格的说是不相等的,后者由于 task() 执行需要耗费不等的时间,所以不存在稳定的间隔,前者可以保障稳定的间隔。
尽管如此,这两种做法都适用于实际需求场景。
但在实际编程时,由于切入点更多,后面的做法明显优于前者。
setTimeout(function f(){ task() if(xxx) setTimeout(f, 1000) // else 则不再执行,通过前置条件可以在内部控制是否执行 setTimeout(f, another delay /*可以灵活换间隔*/) setTimeout(f2 /* 可以更换为执行另一个任务 */, 1000) }, 100)
我们看到,
1. 添加一个前置的 if(cond) 就可以控制在满足特定条件时才反复执行。如使用定时器,则变为 if(){task()} else { 空转 },难以让定时器真正停下来。
2. (根据 task 的结果,)可以在下一轮换成其它的延迟间隔。当然,对于定时器来说,也可以在代码内部设置间隔。
3. 可以更换下一轮的任务。
对于使用定时器遇到的常见的场景,开关定时器,使用 setTimeout 也更容易。
function f(){ task() setTimeout(f, 1000) } // 启动反复执行 f() // 停止反复执行 clearTimeout(h) // 但一般都会在 f 内部停止,很少由外部停止
可以看到,启动定时任务和执行一个普通函数没有差别。
另外,在网络编程中,网络任务耗时很难确定,定时器容易造成上一个任务未完成就启动下一个任务,引起并发问题,使用延迟方案则始终在一根线跑,也化解了潜在的并发陷阱。
在其它语言里也有类似的设计,如 java 里有 Executor.schedule(runnable) 和 Executor.scheduleWithInterval() 两个函数,其语义和 setTimeout setInterval 对等。