Accurately run a function when the minute changes? - javascript

How could I accurately run a function when the minute changes? Using a setInterval could work if I trigger it right when the minute changes. But I'm worried setInterval could get disrupted by the event-loop in a long-running process and not stay in sync with the clock.
How can I run a function accurately when the minute changes?

First off, you should use setInterval for repeating timers, since it (tries to) guarantee periodic execution, i.e. any potential delays will not stack up as they will with repeated setTimeout calls. This will execute your function every minute:
var ONE_MINUTE = 60 * 1000;
function showTime() {
console.log(new Date());
}
setInterval(showTime, ONE_MINUTE);
Now, what we need to do is to start this at the exact right time:
function repeatEvery(func, interval) {
// Check current time and calculate the delay until next interval
var now = new Date(),
delay = interval - now % interval;
function start() {
// Execute function now...
func();
// ... and every interval
setInterval(func, interval);
}
// Delay execution until it's an even interval
setTimeout(start, delay);
}
repeatEvery(showTime, ONE_MINUTE);

This may be an idea. The maximum deviation should be 1 second. If you want it to be more precise, lower the milliseconds of setTimeout1.
setTimeout(checkMinutes,1000);
function checkMinutes(){
var now = new Date().getMinutes();
if (now > checkMinutes.prevTime){
// do something
console.log('nextminute arrived');
}
checkMinutes.prevTime = now;
setTimeout(checkChange,1000);
}
1 But, see also this question, about accuracy of timeouts in javascript

You can try to be as accurate as you can, setting a timeout each X milliseconds and check if the minute has passed and how much time has passed since the last invocation of the function, but that's about it.
You cannot be 100% sure that your function will trigger exactly after 1 minute, because there might be something blocking the event-loop then.
If it's something vital, I suggest using a cronjob or a separate Node.js process specifically for that (so you can make sure the event loop isn't blocked).
Resources:
http://www.sitepoint.com/creating-accurate-timers-in-javascript/

I've put up a possible solution for you:
/* Usage:
*
* coolerInterval( func, interval, triggerOnceEvery);
*
* - func : the function to trigger
* - interval : interval that will adjust itself overtime checking the clock time
* - triggerOnceEvery : trigger your function once after X adjustments (default to 1)
*/
var coolerInterval = function(func, interval, triggerOnceEvery) {
var startTime = new Date().getTime(),
nextTick = startTime,
count = 0;
triggerOnceEvery = triggerOnceEvery || 1;
var internalInterval = function() {
nextTick += interval;
count++;
if(count == triggerOnceEvery) {
func();
count = 0;
}
setTimeout(internalInterval, nextTick - new Date().getTime());
};
internalInterval();
};
The following is a sample usage that prints the timestamp once every minute, but the time drift is adjusted every second
coolerInterval(function() {
console.log( new Date().getTime() );
}, 1000, 60);
It's not perfect, but should be reliable enough.
Consider that the user could switch the tab on the browser, or your code could have some other blocking tasks running on the page, so a browser solution will never be perfect, it's up to you (and your requirements) to decide if it's reliable enough or not.

Tested in browser and node.js
sleeps until 2 seconds before minute change then waits for change
you can remove logging as it gets pretty cluttered in log otherwise
function onMinute(cb,init) {
if (typeof cb === 'function') {
var start_time=new Date(),timeslice = start_time.toString(),timeslices = timeslice.split(":"),start_minute=timeslices[1],last_minute=start_minute;
var seconds = 60 - Number(timeslices[2].substr(0,2));
var timer_id;
var spin = function (){
console.log("awake:ready..set..");
var spin_id = setInterval (function () {
var time=new Date(),timeslice = time.toString(),timeslices = timeslice.split(":"),minute=timeslices[1];
if (last_minute!==minute) {
console.log("go!");
clearInterval(spin_id);
last_minute=minute;
cb(timeslice.split(" ")[4],Number(minute),time,timeslice);
console.log("snoozing..");
setTimeout(spin,58000);
}
},100);
};
setTimeout(spin,(seconds-2)*1000);
if (init) {
cb(timeslice.split(" ")[4],Number(start_minute),start_time,timeslice,seconds);
}
}
}
onMinute(function (timestr,minute,time,timetext,seconds) {
if (seconds!==undefined) {
console.log("started waiting for minute changes at",timestr,seconds,"seconds till first epoch");
} else {
console.log("it's",timestr,"and all is well");
}
},true);

My first thought would be to use the Date object to get the current time. This would allow you to set your set interval on the minute with some simple math. Then since your worried about it getting off, every 5-10 min or whatever you think is appropriate, you could recheck the time using a new date object and readjust your set interval accordingly.
This is just my first thought though in the morning I can put up some code(its like 2am here).

This is a fairly straightforward solution ... the interval for the timeout is adjusted each time it's called so it doesn't drift, with a little 50ms safety in case it fires early.
function onTheMinute(callback) {
const remaining = 60000 - (Date.now() % 60000);
setTimeout(() => {
callback.call(null);
onTheMinute(callback);
}, remaining + (remaining < 50 ? 60000 : 0));
}

Here's yet another solution based on #Linus' post and #Brad's comment. The only difference is it's not working by calling the parent function recursively, but instead is just a combination of setInterval() and setTimeout():
function callEveryInterval(callback, callInterval){
// Initiate the callback function to be called every
// *callInterval* milliseconds.
setInterval(interval => {
// We don't know when exactly the program is going to starts
// running, initialize the setInterval() function and, from
// thereon, keep calling the callback function. So there's almost
// surely going to be an offset between the host's system
// clock's minute change and the setInterval()'s ticks.
// The *delay* variable defines the necessary delay for the
// actual callback via setTimeout().
let delay = interval - new Date()%interval
setTimeout(() => callback(), delay)
}, callInterval, callInterval)
}
Small, maybe interesting fact: the callback function only begins executing on the minute change after next.

The solution proposed by #Linus with setInterval is in general correct, but it will work only as long as between two minutes there are exactly 60 seconds. This seemingly obvious assumption breaks down in the presence of a leap second or, probably more frequently, if the code runs on a laptop that get suspended for a number of seconds.
If you need to handle such cases it is best to manually call setTimeout adjusting every time the interval. Something like the following should do the job:
function repeatEvery( func, interval ) {
function repeater() {
repeatEvery( func, interval);
func();
}
var now = new Date();
var delay = interval - now % interval;
setTimeout(repeater, delay);
}

Related

How to execute code exactly 1000 times in 1 second in JavaScript

n = 0;
var timer = setInterval(function() {
if (n == 0) {
console.log(new Date());
}
// execute some other code here
n++;
if (n == 1000) {
clearInterval(timer);
console.log(new Date());
}
}, 1);
This code executes in about 3-4 seconds, depending on machine and browser maybe. How can I make it execute in exactly 1 second?
Javascript timers in browsers are inaccurate (C would be better for that usage).
However, you get a better averaged accuracy having the delay as high as possible, especially avoiding low values, like 1 ms.
It will be difficult to have 1000 evenly timed calls to a function, within one second. One millisecond being a low value , the simple execution of the triggered function itself (plus the overhead of handling timers) is likely to take a time close to 1 ms (or maybe more)... meaning the JS interpreter calls the function after 1ms, executes the code then set a new 1ms timer. Consequently there is more than 1ms between calls.
The JS interpreter does something like
At t call function <-- this takes
execute function <-- some
at t+x set new 1ms timer <-- time
etc...
However if you can afford to end the process within a timeframe closer to 1 second (than the 3-4 seconds you have now), doing as many as possible 1 ms calls, this is possible.
var n = 0;
var timer= setInterval(function(){
if(n++ == 0) {
console.log(new Date());
}
}, 1);
setTimeout(function() {
clearInterval(timer);
console.log("Got n="+n+" at "+(new Date()));
}, 1000);
This is basically the same program as yours
n is incremented every 1ms
however the end of the process is controlled by another 1-second timer
In Chrome, I get 252 n increments and the two dates are ~1 second apart.
Here is a demonstration of the approach for one timer per iteration. It takes roughly 1 second to do 1000 "iterations" of the same callback. The design is rough as it is only an example.
jsFiddle Demo
//Function to compose the array of timers
function timers(count, callback){
var timers = [];
for(var i = 0; i < count; i++){
timers.push(timer(callback,i));
}
return timers;
};
//Function to compose individual timer
function timer(callback, delay){
return function(){
setTimeout(callback,delay);
};
};
//Usage
console.log("Start:",new Date()); //timestamp
var display = document.querySelector("#display");
var settings = { n : 0 };
display.innerHTML = settings.n;
//Arrange timers and callback
var set = timers(1000,function(){
this.n++;
display.innerHTML = this.n;
if(this.n === 1000) console.log("End:",new Date());
}.bind(settings));
//Execute timers
for(var i = 0; i < set.length; i++){ set[i](); }
<div id="display">
</div>
All browsers handle this differently. In most browsers, especially chrome, the default smallest amount of time possible for a task to execute (as in using an interval or timeout) is 4 milliseconds.
The result of the 4ms window is that your 1000 iterations are being done in about 4 seconds. So, clearly this is longer than the desired 1 second in 1000 iterations.
There is not a desirable (possible?) way to accomplish an exact 1 millisecond iteration in JavaScript when executed in a modern browser. The best bet you would have if space (memory and processing power) were not an issue would be to create a timer for each iteration manually and then execute the entire set of them. This of course has its own issues, such as whether or not each task is even executed at the time it was supposed to.
Try the same script in ECMA Script 6
'use strict';
var n = 0;
var timer = setInterval(() => {
n++;
}, 1);
console.log( new Date() );
setTimeout(() => {
clearInterval(timer);
console.log("Final N Value: "+n+" at "+ (new Date()) );
}, 1000);

setInterval with exact timeout

Is there some way to make a function just like the setInterval but the timeout to be exactly the same each time.
In setInterval the timeout varies about the given timeout, a little bit more, a little bit less, but very rare exactly the same.
For example:
var before = new Date().getTime();
setInterval(function() {
var after = new Date().getTime();
newTab.window.location=someURL;
console.log((after - before));
before = after;
}, 50000);
prints 50000,50002, 50005, 50994, 50997, 49999, 50003, 49998 and so on.
I want to be printed always 50000
Javascript is executed in one flow only, so if there is another process doing something at the same time, there's always a chance you timer function will not be executed in time.
If you really need the exact time interval you can block the execution of any other process in advance and hope for the best:
function setExactInterval(handler, time) {
var startTime = Date.now();
setTimeout(function() {
while (true) {
var currentTime = Date.now();
var diff = currentTime - startTime;
if (diff >= time) {
setExactInterval(handler, time);
return handler();
}
}
}, time - 50);
}
It still will not be exact in case the process is blocked by OS though...

Is there an elegant way to implement a clock-update event?

As far as I know (and I know relatively little), there is no native event that is emitted when e.g. the seconds hand ticks. The best I came up with is repeatedly checking a Date object (e.g. every 333ms; a shorter interval results in higher precision but is also more resource intensive). Somehow, if I use the same Date object over and over, time won't update, while
Date.prototype.getSeconds()
logs 'NaN' although typeof is 'number'.
function clock(interval) {
var d = new Date();
var secondsOld = d.getSeconds();
d = null;
setInterval(function() {
var d = new Date();
var secondsNew = d.getSeconds();
if ( secondsNew !== secondsOld ) {
secondsOld = secondsNew;
// trigger something
console.log(secondsOld);
}
d = null;
}, interval);
}
You're right in that there are no native clock tick events. The most performant while precise way I would approach this issue is using setTimeout() and Date.now().
Create a recursive function that calls setTimeout() every second. To be precise, make the timeout the duration until the next exact second from that function call. Here's an example.
// milliseconds per second
var SECOND = 1000;
function bindClockTick(callback) {
function tick() {
var now = Date.now();
callback(now);
setTimeout(tick, SECOND - (now % SECOND));
}
tick();
}
bindClockTick(function(ms) {
console.log('tick! milliseconds: '+ ms);
});
This uses Date.now() instead of creating a new instance of the Date class.
Here's a JSFiddle to test the precision. The demo uses new Date() to easily display the current time, but it can be done with just milliseconds.
I'm not sure why you would want to sync with the exact second change; however, here's how I would do it:
function clock() {
var startMs = Date.now(),
startSecs = Math.floor(startMs / 1000),
firstOffset = 1000 - startMs % 1000;
function tick () {
var ms = Date.now(),
secs = Math.floor(ms / 1000),
dSecs = secs - startSecs;
console.log(dSecs);
}
setTimeout(function () {
tick();
setInterval(tick, 1000);
}, firstOffset);
tick();
}
clock();
Here's what happens:
I grab the current time in ms with Date.now()
I figure out how many ms until the next second tick (firstOffset)
I set the initial setTimeout to this offset, making sure that it will fire on the next second tick.
Now that we are synced with the second tick, setInterval with 1000 ms will continue to update every second.

Timing in JS - multiple setIntervals running at once and starting at the same time?

Let's say I have a function:
myFunc = function(number) {
console.log("Booyah! "+number);
}
And I want it to run on a set interval. Sounds like I should use setInterval, huh!
But what if I want to run multiple intervals of the same function, all starting at the exact same time?
setInterval(function(){
myFunc(1);
}, 500);
setInterval(function(){
myFunc(2);
}, 1000);
setInterval(function(){
myFunc(3);
}, 2000);
So that the first runs exactly twice in the time it takes the second to run once, and the same between the second and third.
How do you make sure that they all start at the same time so that they are in sync?
Good question, but in JS you can't. To have multiple functions in the same program execute at the same time you need multi-threading and some deep timing and thread handling skills. JS is single threaded. setInterval doesn't acutally run the function after the delay, rather after the delay it adds the function to the event stack to be run as soon as the processor can get to it. If the proc is busy with another operation, it will take longer than the delay period to actually run. Multiple intervals/timeouts are all adding calls to the same event stack, so they run in turn as the proc is available.
function Timer(funct, delayMs, times)
{
if(times==undefined)
{
times=-1;
}
if(delayMs==undefined)
{
delayMs=10;
}
this.funct=funct;
var times=times;
var timesCount=0;
var ticks = (delayMs/10)|0;
var count=0;
Timer.instances.push(this);
this.tick = function()
{
if(count>=ticks)
{
this.funct();
count=0;
if(times>-1)
{
timesCount++;
if(timesCount>=times)
{
this.stop();
}
}
}
count++;
};
this.stop=function()
{
var index = Timer.instances.indexOf(this);
Timer.instances.splice(index, 1);
};
}
Timer.instances=[];
Timer.ontick=function()
{
for(var i in Timer.instances)
{
Timer.instances[i].tick();
}
};
window.setInterval(Timer.ontick, 10);
And to use it:
function onTick()
{
window.alert('test');
}
function onTick2()
{
window.alert('test2');
}
var timer = new Timer(onTick, 2000,-1);
var timer = new Timer(onTick2, 16000,-1);
For a finite number of ticks, change the last parameter to a positive integer for number. I used -1 to indicate continuous running.
Ignore anyone who tells you that you can't. You can make it do just about any thing you like!
You can make something like this.
arr = Array();
arr[0] = "hi";
arr[1] = "bye";
setTimer0 = setInterval(function(id){
console.log(arr[id])
},1000,(0));
setTimer1 = setInterval(function(id){
console.log(arr[id]);
},500,(1));
Hope it helps!
JavaScript is single threaded. You can use html5 web worker or try using setTimeout recursively. Create multiple functions following this example:
var interval = setTimeout(appendDateToBody, 5000);
function appendDateToBody() {
document.body.appendChild(
document.createTextNode(new Date() + " "));
interval = setTimeout(appendDateToBody, 5000);
}
Read this article:
http://weblogs.asp.net/bleroy/archive/2009/05/14/setinterval-is-moderately-evil.aspx
You can use multiples of ticks inside functions, in the example below you can run one function every 0.1 sec, and another every 1 sec.
Obviously, the timing will go wrong if functions require longer times than the intervals you set. You might need to experiment with the values to make them work or tolerate the incorrect timing.
Set a variable to handle tick multiples
let tickDivider = -1
Increase the value of tick variable inside the faster function
const fastFunc = ()=> {
tickDivider += 1
console.log('fastFunciton')
}
Use a condition to on running the slower function
const slowFunc = ()=> {
if (!(tickDivider % 10)){
console.log('slowFunction')
}
}
Call both functions in a single one. The order is not important unless you set tickDivider to 0 (of any multiple of 10)
const updateAllFuncs = () => {
fastFunc()
slowFunc()
}
Set the interval to the frequency of the faster function
setInterval(updateAllFuncs, 100)
What I'm doing here is adding a speed attribute to the HTML elements. These speeds are passed as a parameter to setCounter(). I did this mainly to make the code easier to test and play with.
The function setCounter() is invoked inside a loop for every HTML element with class counter. This function sets a new setInterval in every execution.
The intervals seem to be working in sync.
const elements = document.querySelectorAll('.counter')
elements.forEach((el, i) => {
let speed = Number(elements[i].getAttribute('speed'))
setCounter(el, speed, 5000)
})
function setCounter(element, speed, elapse){
let count = 0
setInterval(() => {
count = (count >= elapse) ? elapse : count + speed
if(count === elapse) clearInterval()
element.innerHTML = count
}, 1)
}
Same Speeds
<p class="counter" speed='10'></p>
<p class="counter" speed='10'></p>
Different Speeds
<p class="counter" speed='3'></p>
<p class="counter" speed='5'></p>

Why does setTimeout() "break" for large millisecond delay values?

I came across some unexpected behavior when passing a large millisecond value to setTimeout(). For instance,
setTimeout(some_callback, Number.MAX_VALUE);
and
setTimeout(some_callback, Infinity);
both cause some_callback to be run almost immediately, as if I'd passed 0 instead of a large number as the delay.
Why does this happen?
This is due to setTimeout using a 32 bit int to store the delay so the max value allowed would be
2147483647
if you try
2147483648
you get your problem occurring.
I can only presume this is causing some form of internal exception in the JS Engine and causing the function to fire immediately rather than not at all.
You can use:
function runAtDate(date, func) {
var now = (new Date()).getTime();
var then = date.getTime();
var diff = Math.max((then - now), 0);
if (diff > 0x7FFFFFFF) //setTimeout limit is MAX_INT32=(2^31-1)
setTimeout(function() {runAtDate(date, func);}, 0x7FFFFFFF);
else
setTimeout(func, diff);
}
Some explanation here: http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html
Timeout values too big to fit into a signed 32-bit integer may cause
overflow in FF, Safari, and Chrome, resulting in the timeout being
scheduled immediately. It makes more sense simply not to schedule these
timeouts, since 24.8 days is beyond a reasonable expectation for the
browser to stay open.
Check out the node doc on Timers here: https://nodejs.org/api/timers.html (assuming same across js as well since it's such an ubiquitous term now in event loop based
In short:
When delay is larger than 2147483647 or less than 1, the delay will be set to 1.
and delay is:
The number of milliseconds to wait before calling the callback.
Seems like your timeout value is being defaulted to an unexpected value along these rules, possibly?
I stumbled on this when I tried to automatically logout a user with an expired session. My solution was to just reset the timeout after one day, and keep the functionality to use clearTimeout.
Here is a little prototype example:
Timer = function(execTime, callback) {
if(!(execTime instanceof Date)) {
execTime = new Date(execTime);
}
this.execTime = execTime;
this.callback = callback;
this.init();
};
Timer.prototype = {
callback: null,
execTime: null,
_timeout : null,
/**
* Initialize and start timer
*/
init : function() {
this.checkTimer();
},
/**
* Get the time of the callback execution should happen
*/
getExecTime : function() {
return this.execTime;
},
/**
* Checks the current time with the execute time and executes callback accordingly
*/
checkTimer : function() {
clearTimeout(this._timeout);
var now = new Date();
var ms = this.getExecTime().getTime() - now.getTime();
/**
* Check if timer has expired
*/
if(ms <= 0) {
this.callback(this);
return false;
}
/**
* Check if ms is more than one day, then revered to one day
*/
var max = (86400 * 1000);
if(ms > max) {
ms = max;
}
/**
* Otherwise set timeout
*/
this._timeout = setTimeout(function(self) {
self.checkTimer();
}, ms, this);
},
/**
* Stops the timeout
*/
stopTimer : function() {
clearTimeout(this._timeout);
}
};
Usage:
var timer = new Timer('2018-08-17 14:05:00', function() {
document.location.reload();
});
And you may clear it with the stopTimer method:
timer.stopTimer();
Can't comment but to answer all the people.
It takes unsigned value ( you can't wait negative milliseconds obviously )
So since max value is "2147483647" when you enter a higher value it start going from 0.
Basically delay = {VALUE} % 2147483647.
So using delay of 2147483648 would make it 1 millisecond, therefore, instant proc.
Number.MAX_VALUE
is actually not an integer. The maximum allowable value for setTimeout is likely 2^31 or 2^32. Try
parseInt(Number.MAX_VALUE)
and you get 1 back instead of 1.7976931348623157e+308.

Categories

Resources