关于js中setTimeout和setInterval的定时上限值

2019年04月27日Web前端0

在使用node爬虫的时候接触到了node-schedule,觉得node-schedule蛮是不错,于是便悄咪咪的查看起了源码,看着看着就跟到了一个叫long-timeout的库中,也了解到了个小知识点。

node-schedule

node-schedule是node中的一个定时器模块,他支持多种方式的定时方式,用起来很是顺手。便抽空看了下他的源码。源文件位于node_module中的node-schedule文件夹,在lib下有个schedule.js文件。

当阅读到runOnDate这个方法时,发现他计算当前和触发时间点的差值,再使用lt.setTimeout方法延迟到对应时间点再触发。于是便纳闷,直接setTimeout不就完事了么?干嘛还得再调个库,于是定位到long-timeout这个依赖文件中。

long-timeout

该依赖比较简单,直接打开目录下的index.js,第一行代码便是:

var TIMEOUT_MAX = 2147483647; // 2^31-1

难道定时的最大值时2的31次方减1?接下去看,整个代码分为setTimout和setInterval两部分。外部调用该文件的setTimout方法时,new个实例,并调用了原型上的start方法:

Timeout.prototype.start = function() {
    if (this.after <= TIMEOUT_MAX) {
        this.timeout = setTimeout(this.listener, this.after)
    } else {
        var self =this
        this.timeout = setTimeout(function() {
            self.after -= TIMEOUT_MAX
            self.start()
        }, TIMEOUT_MAX)
    }
    if (this.unreffed) {
        this.timeout.unref()
    }
}

after表示多久后触发,当该值小于等于timeout的最大值时,就直接使用window下的setTimeout方法。如果after值大于了timeout的最大值,修改after值,减去了延迟时间,并以timeout最大值做延迟时间,调用window下的setTimeout方法,并在触发后继续调用start方法去检测时间。

到这的话,大概就明白了,当延迟小于等于2^31-1,直接用window的setTimeout没有问题。但时间大于这个值时,就有问题了,需要每次定时2^31-1时间,再做一次时间检测,若小则使用window的setTimout方法,否则继续延迟2~31-1时间。

测试

现在我们有了最大值了,来做个简单的测试setInterval一样,只显示setTimeout了 :

var time = Math.pow(2, 31);

setTimeout(function() {
    console.log(1);
}, time);

给极限值加1,我们试下会有啥结果,打开控制台,已经有1这个打印了,证明当超过定时上限时,会直接输出。再次修改代码:

var time = Math.pow(2, 31);

setTimeout(() => console.log(4), 0);
setTimeout(() => console.log(5));
setTimeout(() => console.log(1), time);
setTimeout(() => console.log(3), 0);
console.log(2);

控制台上输出的是2, 4, 5, 1, 3,所以当超过极限值时,该定时和设置0一样,会立即执行。

总结

js中setTimout和setInterval是有定时极大值的,他是2^31-1,即2147483647。当超过这个值时,与不给他设置定时值效果一致,即会立即触发(执行栈空了时)。

好了,又一个知识点get了。详细的demo请查看:github