bushman
bushman

Reputation: 647

setInterval By the minute On the minute

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

Answers (12)

Soubhagya Kumar Barik
Soubhagya Kumar Barik

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

Brandon McConnell
Brandon McConnell

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

J_sdev
J_sdev

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

Apps-n-Add-Ons
Apps-n-Add-Ons

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

Lajos Arpad
Lajos Arpad

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

Prabhakar Poudel
Prabhakar Poudel

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();

  1. Get the seconds left to reach a minute secondsLeft = 60 - date.getSeconds();
  2. 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);
    
  3. Using above format/algorithm you should be able to achieve similar problems be the seconds supplied in any format. The idea is to get the difference of seconds and 60, run a timeout after those seconds, and inside timeout function run interval function with interval as a minute/or anything as such based on requirement.

Upvotes: 1

Bartłomiej Mucha
Bartłomiej Mucha

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

Joel
Joel

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

plodder
plodder

Reputation: 2306

function waitForNewMinute() {
    if (new Date().getSeconds()>1) {
        setTimeout(waitForNewMinute, 500);
    } else {
        setInterval(doMyThing,60000);
    } 
}

waitForNewMinute();

Upvotes: 0

Pointy
Pointy

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

goat
goat

Reputation: 31834

var d = new Date();
var milisecondsUntilMinuteChanges = 60000 - d.getMilliseconds() - (1000 * d.getSeconds());

Upvotes: 1

Stefan Kendall
Stefan Kendall

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

Related Questions