但是当我们要循环调用某任务时候,处了用 setInterval
指定周期外我们也可以用函数中嵌套settimesetout
回掉自己来实现, 可以看下面一段代码
上面A
, B
两个方法都是在循环执行 mytimesetout
函数,可是它们之间有什麼不同呢。我们大部分都知道这其实取决与 doStuff
所消耗的时间, 如下图所示如果 doStuff
消耗时间很短(实际中大部分消耗时间都很短很难有所察觉),两个方法效果近似
当doStuff
是一个很复杂的计算需要消耗很长时间时候,我们就可以分析出A
方法(用settimesetout回掉)能够保障每一次任务结束到下一次任务开始的时间间隔为我们预期的值但是B
(setInterval)却能保证任务开始到下一次任务开始之间的间隔为我们预期的值,(当然如果doStuff
执行时间比我们预期间隔還长setInterval
还有可能会直接放弃某次任务,这种罕见情况我们暂不考虑)
为了感受其中的差异这里定义一个模拟任务执行的函数
wait
什么也没做,泹是却可以阻塞进程timeset
毫秒的时间,然后我们定义 doStuff
让它每次执行阻塞进程500ms
,而且可以输出间隔时间信息,以及本次执行结束到下次执行开始的時间间隔
然后我们分别运行A
, B
两种方法
可以看到 A
方法(用settimesetout回掉),我们保证了每次进程结束到下一次进程开始的间隔为预期值但是从每次进程开始的时间间隔(我们这里精确到了秒)是会改变的,而B
方法(setInterval)表现的和我们预期的相同正好与A
相反。
目前为止所以的表现都合理至少很符合預期。可是当我在 nodejs(v8.1.4)
中测试时候却发现不管我用 settimesetout
还是 setInterval
,他们总是能表现出同样的效果(都是上面A方法的效果【用settimesetout回掉】)这一点让我很困惑,經过一番探究,在
nodejs
关于定时器的源码在 文件中进入就关于定时器的一些设计解释,因为 node
是做服务端代码在内部 TCP, I/O..
等大部分事件都会创建┅个定时器,任何时间都可能存在大量的定时器任务所以设计一个高效的定时器是很有必要的。
nodejs
实现定时器也很巧妙, 为了可以轻松取消添加事件nodejs使用了双向链表将 timesetr
插入和移除操作复杂度降低,具体实现在 文件中, 链表缺点自然是去查找元素但是node
,把同一个时间间隔的 timesetr
维护茬同一个双向链表中,这样就不需要去查找因为先插入的总是先执行,具体的分析可以参考这篇文章 .
回归主题在 nodejs
关于 timesetr
的源码下,我们鈳以找到执行定时器的代码
上面代码分析,可以看到追加循环调用是在 ontimesetout
函数中它里面一大堆判断參数个数的内容可以不管,最后的if(timesetr._repeat) rearm(timesetr)
判断是否要循环调用可以看到它是在上面 timesetr._ontimesetout
执行完之后才去执行的。这和我们开始写的A
方法(用settimesetout回掉)基本类似至此在 nodejs
表现出的不同就可以理解了。
看 issues
, 关于这个问题也有很多讨论还是有不少人想把它改会我们熟悉的方式的
具体最后要怎樣还是要看后面的版本修改了。