Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Accurate Javascript setInterval replacement
function interval(duration, fn){
var _this = this
this.baseline = undefined
this.run = function(){
if(_this.baseline === undefined){
_this.baseline = new Date().getTime()
}
fn()
var end = new Date().getTime()
_this.baseline += duration
var nextTick = duration - (end - _this.baseline)
if(nextTick<0){
nextTick = 0
}
_this.timer = setTimeout(function(){
_this.run(end)
}, nextTick)
}
this.stop = function(){
clearTimeout(_this.timer)
}
}
@manast

This comment has been minimized.

Copy link
Owner Author

@manast manast commented Sep 1, 2011

This code snippet can be used when a very accurate setInterval is needed. It will always be accurate ( 1+-ms in variance, but the mean will be the exact duration). Of course the function provided must run faster than the duration parameter. If for some reason the function takes more time than the duration in some occasions, the timer will still converge to the exact intervals very fast.

Example of use:

var timer = new interval(50, function(){
      console.log(new Date().getTime())
    })
    timer.run()
@sessa

This comment has been minimized.

Copy link

@sessa sessa commented Jun 21, 2012

Did you snag this from another programming language?

@manast

This comment has been minimized.

Copy link
Owner Author

@manast manast commented Jun 21, 2012

I did not, but this was written when I was quite new in javascript. I have a better version of this code in our framework: https://github.com/OptimalBits/ginger/blob/master/ginger.js

But of course it depends on other things, it is not lightweight as the one in the gist. In any case I am planing a new interval/timer that is a bit more powerful, I want to be able to keep just one timer for all the intervals instances.

@ezabba

This comment has been minimized.

Copy link

@ezabba ezabba commented Jul 26, 2012

on line 20 shouldn't be

})(this)

instead of

}(this))

?

@ArnaudRinquin

This comment has been minimized.

Copy link

@ArnaudRinquin ArnaudRinquin commented Nov 28, 2012

You are tight ezabba

@ArnaudRinquin

This comment has been minimized.

Copy link

@ArnaudRinquin ArnaudRinquin commented Nov 28, 2012

right*

@tanepiper

This comment has been minimized.

Copy link

@tanepiper tanepiper commented Dec 5, 2012

@manast - I've implemented a new version of this at https://gist.github.com/4215634

Rather than calculate the difference between an updated baseline, this instead takes a start time and current time, and calculates a pretty accurate tick value to compensate for drift - I've had this running for hours and keep accurate to within 1-5ms.

A bonus of this version if you use the default 1000ms (1s) tick value, it also gives you an accurate H/M/S timing, as shown in the timer example

@german-bortoli

This comment has been minimized.

Copy link

@german-bortoli german-bortoli commented May 28, 2015

This has a problem, when the window lose the focus, the timers speed up after focus again.

@weednation12

This comment has been minimized.

Copy link

@weednation12 weednation12 commented Nov 5, 2015

Good code, integrated it and works perfect!

@englishextra

This comment has been minimized.

Copy link

@englishextra englishextra commented Jun 12, 2016

@manast
I would add

timer && timer.run();

Why? Consider the code below:

        var si = new interval(50, function () {
                if ("undefined" !== typeof hiddenPreloadImage && hiddenPreloadImage && 0 !== si) {
                    si.stop(),
                    si = 0;
                    setStyleOpacity(superbox, 1),
                    setImmediate(function () {
                        window.progressBar && (progressBar.finish(), progressBar.hide());
                    });
                }
            });
        si && si.run();
@jsp1987

This comment has been minimized.

Copy link

@jsp1987 jsp1987 commented Aug 8, 2018

I never thought that JavaScript setInterval wasn't perfectly accurate. I applied interval.js in my website and it worked perfectly. Thank you!

@fraktalfabrik

This comment has been minimized.

Copy link

@fraktalfabrik fraktalfabrik commented Nov 22, 2019

I never thought that JavaScript setInterval wasn't perfectly accurate. I applied interval.js in my website and it worked perfectly. Thank you!

try creating a metronome and you'll soon find out how inaccurate it is ;)
http://music.lighting/Metronome/

@morganruffner

This comment has been minimized.

Copy link

@morganruffner morganruffner commented Aug 7, 2020

I am trying to implement this but it does not stop when this.interval.stop() is called, is there any reason why this could be? I am creating it with: that = this;
this.interval = new interval(that.state.timerint, function(){
that.noMetCount()
})

@manast

This comment has been minimized.

Copy link
Owner Author

@manast manast commented Aug 8, 2020

@morganruffner try the latest version instead. I just updated the code.

@morganruffner

This comment has been minimized.

Copy link

@morganruffner morganruffner commented Aug 11, 2020

@manast Unfortunately I still haven't been able to get it to work, I see that it is going into the stop function but the function continues being called.

@dehaotu

This comment has been minimized.

Copy link

@dehaotu dehaotu commented Oct 24, 2020

I am trying to implement this but it does not stop when this.interval.stop() is called, is there any reason why this could be? I am creating it with: that = this;
this.interval = new interval(that.state.timerint, function(){
that.noMetCount()
})

function interval(duration, fn){
  var _this = this
  _this.baseline = undefined
  
  _this.run = function(){
    if(_this.baseline === undefined){
      _this.baseline = new Date().getTime()
    }
    fn()
    _this.end = new Date().getTime()
    _this.baseline += duration
 
    _this.nextTick = duration - (_this.end - _this.baseline)
    if(_this.nextTick<0){
      _this.nextTick = 0
    }

    if (_this.run !== undefined) {
      _this.timer = setTimeout(function(){
        _this.run()
      }, _this.nextTick)
    }
  }

  _this.stop = function(){
    clearTimeout(_this.timer);
    _this.run = undefined;
  }

This works for me. What I assume has happened was, you were trying to call this.interval.stop() inside fn(), but in this case _this.run() will keep executing and creating a new setTimeout and assigned to _this.timer. To counter this, I marked _this.run as undefined in _this.stop, and before the assignment for _this.timer in the _this.run, I check if _this.run is still defined.

I hope this works for other people as well... Please correct me if I'm wrong.

@ihewro

This comment has been minimized.

Copy link

@ihewro ihewro commented Apr 17, 2021

In the first time,it wait double douration, there is the fixed version and with the args:

export function Interval(fn,duration,...args){
    const _this = this;
    this.baseline = undefined

    this.run = function(flag){
        if(_this.baseline === undefined){
            _this.baseline = new Date().getTime() - duration
        }
        if (flag){
            fn(...args);
        }
        const end = new Date().getTime();
        _this.baseline += duration

        let nextTick = duration - (end - _this.baseline);
        if(nextTick<0){
            nextTick = 0
        }

        console.log(nextTick);
        _this.timer = setTimeout(function(){
            _this.run(true)
        }, nextTick)
    }

    this.stop = function(){
        clearTimeout(_this.timer)
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment