第 133 题:用 setTimeout 实现 setInterval,阐述实现的效果与 setInterval 的差异?
当中应该涉及 setInterval 的特性,node 环境与浏览器的又会作何表现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
当中应该涉及 setInterval 的特性,node 环境与浏览器的又会作何表现。
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(36)
我感觉这样的模拟实现会让定时器误差变大,每次调用setTimeout开启定时器也有一定的时间消耗.
看issue别的大佬的回复, 设置的间隔时间过短的时候,如果代码块里的代码并没有执行完也会重新开始执行,但使用setTimeout实现setInterval的这个效果就没这个问题,必然会把代码块中的代码运行完后,然后才会再次调用该函数.
hahaha...(6个时候)
[Done] exited with code=1 in 7.024 seconds
hahaha...(19个时候)
[Done] exited with code=1 in 19.326 seconds
(不过这个测试不够准确.......)
hugeorange 的写法有问题,函数应该允许反复调用,如果在函数上定义timer变量,当我多次调用方法时,timer永远是最后一次的,这会导致之前的定时器不能正确使用clear方法。
我的改写:
另外很多人写的有点问题,如果我们在fn里面执行clear计时器操作,那么必须将重启timer操作前置,不然将导致clear失败;比如上面代码如果写成:
当我在fn内做了clear,但是loop紧接着会重启,这导致clear操作失败。
测试用例:
模拟的setInterval 和原生setInterval 没有差异?
setInterval能够保证以固定频率向事件队列放入回调,setTimeout不能保证。两个都不能保证固定的回调执行频率,因为存在主线程阻塞的可能
差异就是假设js执行线程阻塞的话会使自己模拟的间隔增加,而setInterval在页面卡顿时依然会从计时器线程中创建任务增加至任务队列中
有测试过或者能提供相关文献么?
我这里测试 node v10和 chrome 76效果是一样的,即等待前一次代码执行完后立即执行
function mySetInterval(fn,time){
function inner(){
fn();
setTimeout(inner,time);
}
inner()
}
mySetInterval(() => {console.log("sss")}, 1000)
@francecil 我在浏览器 里试验的是 setInterval()如果前一次代码没有执行完,不会跳过此次代码,等待前一次代码执行完后执行。(是不是立即,还有待测试
结果:
预备知识
在浏览器中,setInterval的方法定义为:
可以看出,该方法返回的句柄是不变的 long 值,我们需要通过该句柄去取消定时器
另一个要注意的点是:该方法的执行上下文必须为 window,WorkerUtils ,或者 实现
WindowTimers interface
的对象(这个目前不知道怎么实现)注意:
clearInterval(lone handler)
会对 handler 做隐式类型转换,下文有用到该特性而在node环境中, setInterval 返回的是一个 Timeout 对象,
clearInterval(object timer)
, 故 clearInterval 不会对其中的参数做隐式类型转换(https://github.com/nodejs/node/blob/master/lib/timers.js)setTimeout 模拟
setTimeout 模拟
setInterval(handler,?timeout,...args)
,有两种实现:测试用例:
setInterval 、 setInterval1 、 setInterva2 三者差异对比
先编写预处理函数
1. handler 为同步处理函数
当 handler 为同步处理函数且执行时间小于 timeout,我们可以得到以下结论:
通过 node-libuv 源码可以证明
function mySetInterval() {
mySetInterval.id = setTimeout(() => {
arguments0
mySetInterval(...arguments)
}, arguments[1])
}
mySetInterval.clearInterval = function (intervalId) {
clearTimeout(intervalId)
}
mySetInterval( () => {
console.log('1')
}, 1000)
setTimeout(() => {
mySetInterval.clearInterval(mySetInterval.id)
}, 5000)
@hugeorange 这种实现与setInterval的差异呢?
timerFun();
function timerFun() {
console.log('1');
var timer = setTimeout(function() {
timerFun();
clearTimeout(timer)
}, 1000)
}
大概实现了一下?返回值和原生略有不同使用了对象方便访问:
只有你提到了setInterval的那个痛点..
带清理功能的
// 区别
自己实现的的setTimerout回调版只能打印9次,会越来越慢。原版setInterval能打印10次
受下面那位老哥@weiweixuan的启发 ,改了下代码,实现了清理功能
function timeout(fn,time){ var timer; if(timer){ clearTimeout(timer) } return function() { timer = setTimeout(function(){ fn(); timeout(fn,time)() },time); } } function interval (fn,time) { timeout(fn,time)() }