Reputation: 647
To the javascript enthusiasts,
how would you program a setTimeOut
(or setInterval
) handle to fire by the minute on the minute. So for example, if it is the 51 second of the current time, then fire it in 9 seconds, if it is the 14th second then fire it in 46 seconds
thanks
Upvotes: 20
Views: 53198
Reputation: 2605
The Above answers fulfill the job but still wanted to solve this with a better approach.
You can solve this by using setInterval
& clearInterval
let job = setInterval(function() {
/* Do you stuff */
},1000);
setTimeout(function() {
clearInterval(job);
},60000);
Upvotes: 0
Reputation: 6129
This can actually be done fairly simply by combining setTimeout
with setInterval
. I'll first demonstrate how to do this in long-form, and then provide a shorthand one-liner function below which will accomplish the same thing.
function setEmomInterval(expr, ...rest) {
setTimeout(function() {
expr(...rest);
setInterval(expr, 60000, ...rest);
}, 60000 - new Date().getTime() % (60 * 1000));
}
setEmomInterval(() => console.log(`The time is now ${new Date().getHours()}:${new Date().getMinutes()}`));
This has the added benefit of not only being accurate to the second, but in fact accurate to the millisecond, minus any minimal lag due to computation time.
As a one-liner, it would look like this:
const setEmomInterval = (expr, ...rest) => setTimeout(() => (expr(...rest), setInterval(expr, 60000, ...rest)), 60000 - new Date().getTime() % (60 * 1000));
setEmomInterval(() => console.log(`The time is now ${new Date().getHours()}:${new Date().getMinutes()}`));
As mentioned, there is some delay due to what is known as "drift" in the computation time. To work around this, if you need the most accurate time, use the driftless library by Dan Kaplun (@dbkaplun on GitHub).
Upvotes: 0
Reputation: 346
I believe all the answers are flawed. Once you have started the setInterval, the interval time cannot be changed without creating a new setInterval object. So you will only use the initial calculated time for each iteration. You can create new setInterval objects, but that has to be costly.
See: How do I reset the setInterval timer?
My recommendation is to create a looping function that utilizes the setTimeout instead.
Here's some code:
var duration = 5; //in minutes. This is just to control the total length
var startTime = new Date().getTime(); //must be in milliseconds
function myOnTheMinuteTimer(){
setTimeout(function(){
var currentTimeMS = new Date().getTime();
var diffMs = Math.abs(startTime - currentTimeMS);
diffMs = diffMs/1000; //take out the milliseconds
var seconds = Math.floor(diffMs % 60);
diffMs = diffMs/60;
var minutes = Math.floor(diffMs % 60);
var minutesLeft = duration - minutes;
console.log("Time passed:", minutes + "m :" + seconds + "s");
if(minutes <= duration){
console.log("Timer expired.");
} else {
myOnTheMinuteTimer();
}
}, setIntervalTimeToNextMinute());
}
//Initialize timer
myOnTheMinuteTimer();
//Get our time until the next minute
function setIntervalTimeToNextMinute(){
var currentDateSeconds = new Date().getSeconds();
if(currentDateSeconds == 0){
return 60000;
} else {
return (60 - currentDateSeconds) * 1000;
}
}
My use case for this was a session timout:
startTime - allows me to pass in any milliseconds time. This way I can avoid lapses in page loading. Say user logins in at a certain time. I capture that time, then pass it into initial function and it will adjust back to the on minute calculation.
duration - I use the duration check for a timeout setting. If the application has a 20 minute timeout, then I need to route the user to the login screen.
optionally - you can add more variations. Say you wish to give the user a 2 minute warning that they will be logged out. Simply add to the conditional an else if for 2 minutes.
Hope that helps! Happy coding!
Upvotes: 0
Reputation: 2146
I used the ' (60 - date.getSeconds()) * 1000);' method on a data refresh function and, while it worked, it 'drifted' over time (14 seconds late before I changed it!)
As the data needed to be updated ON the minute (and that was obviously not happening as we have a clock on the same page!), here's the solution I came up with:
setInterval(function () {
if(moment().format('ss') === "00"){
console.log('we are at the 0 - update the data!');
refresh_data();
}
}, 1000);
This of course uses moment.js to find the '0' time and it certainly does what the OP asked - fires on the minute - every time!
Upvotes: 0
Reputation: 76905
First, let's make sure we know how much time do we have until the next minute:
var toExactMinute = 60000 - (new Date().getTime() % 60000);
Now, if you want to work with setTimeout
, then
setTimeout(yourfunction, toExactMinute);
If you want to do a setInterval
:
setTimeout(function() {
setInterval(yourFunction, 60000);
yourFunction();
}, toExactMinute);
Upvotes: 10
Reputation: 339
Although too late for the response, I have had similar problem to solve recently and thought of sharing my approach. My problem was to run a timeout after a specified seconds based on how far it is away from a minute, and subsequently run interval function each minute. Here is what I did.
Let's say the date is javascript date object created using date = new Date();
secondsLeft = 60 - date.getSeconds();
Then set a timeout function to run after secondsLeft
, and inside the function set an interval function to run each minute as:
setTimeout(function() {
// perform the action **A**;
minuteMS = 60 * 1000; // seconds * milliSeconds
setIntervalId = setInterval(function() {
// perform the action **A**;
}, minuteMS);
}, secondsLeft);
Upvotes: 1
Reputation: 2782
At first, I used the solution proposed by Joel Potter. His solution is good enough in most cases. However, because the javascript is doing one thing at a time (unless you are using Workers), it may be late with first timeout, so your first timeout is executed eg. 5 seconds too late and you get interval on every 5 seconds after the minute.
So, here is my implementation:
function setMyFunctionInterval()
{
var currentDateSeconds = new Date().getSeconds();
if (currentDateSeconds == 0) {
setInterval(myFunction, 60000);
}
else {
setTimeout(function () {
setMyFunctionInterval();
}, (60 - currentDateSeconds) * 1000);
}
myFunction();
}
setMyFunctionInterval();
Upvotes: 3
Reputation: 19368
var date = new Date();
setTimeout(function() {
setInterval(myFunction, 60000);
myFunction();
}, (60 - date.getSeconds()) * 1000);
Obviously this isn't 100% precise, but for the majority of cases it should be sufficient.
Upvotes: 26
Reputation: 2306
function waitForNewMinute() {
if (new Date().getSeconds()>1) {
setTimeout(waitForNewMinute, 500);
} else {
setInterval(doMyThing,60000);
}
}
waitForNewMinute();
Upvotes: 0
Reputation: 413866
var nextTick = function() {
return 60000 - (new Date().getTime() % 60000);
}, timerFunction = function() {
// do stuff
// do stuff
// do stuff
setTimeout(timerFunction, nextTick());
};
var timeout = setTimeout(timerFunction, nextTick());
Upvotes: 8
Reputation: 31834
var d = new Date();
var milisecondsUntilMinuteChanges = 60000 - d.getMilliseconds() - (1000 * d.getSeconds());
Upvotes: 1
Reputation: 67872
var seconds = (new Date()).getSeconds();
Use 60-seconds
as the timeout time. Granted, setTimeout
lets you specify milliseconds, which means you should also be checking milliseconds, but this will give you "close enough" for most applications. If anything, this should make you overshoot the minute some fraction of a second, but it's better to overshoot than undershoot. Undershooting would cause catastrophic failure with this method.
Upvotes: 1