JavaScript: How to do something every full hour?
Asked Answered
A

17

20

I want to execute some JS code every hour. But I can't use

setInterval("javascript function",60*60*1000);

because I want to do it every full hour, I mean in 1:00, in 2:00, in 3:00 and so on. I am thinking about something like

var d;
while(true) {
  d = new Date();
  if ((d.getMinutes() == '00') && (d.getSeconds() == '00')){
    // my code here
  }  
}

but it's too slow and it doesn't work well.

Thak you for any ideas

Aaberg answered 6/9, 2012 at 22:0 Comment(2)
"too slow" would be an euphemism, right? That would completely freeze your browser forever!Alisaalisan
What is the context? Seems like there might be a nicer solution on the server with a Cron job or something.... but hard to tell, w/o knowing what it's for.Intumescence
P
34

I would find out what time it is now, figure out how long it is until the next full hour, then wait that long. So,

function doSomething() {
    var d = new Date(),
        h = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() + 1, 0, 0, 0),
        e = h - d;
    if (e > 100) { // some arbitrary time period
        window.setTimeout(doSomething, e);
    }
    // your code
}

The check for e > 100 is just to make sure you don't do setTimeout on something like 5 ms and get in a crazy loop.

Prager answered 6/9, 2012 at 22:7 Comment(0)
E
11

What you can do is have an interval run every minute, and check if the time is :00 before running the actual code.

Exude answered 6/9, 2012 at 22:3 Comment(0)
W
11

look at this one :

function to_be_executed(){
    ...
    ...
    ...
    makeInterval();
}

function makeInterval(){
    var d = new Date();
    var min = d.getMinutes();
    var sec = d.getSeconds();

    if((min == '00') && (sec == '00'))
        to_be_executed();
    else
        setTimeout(to_be_executed,(60*(60-min)+(60-sec))*1000);
}
Witting answered 6/9, 2012 at 22:9 Comment(1)
You need to remember to clear the old interval before setting the new one...otherwise nice work.Eidetic
T
6

ES6 version, works in NodeJS and all modern browsers - executes on millisecond according to browser or server clock:

---------- UPDATED ----------

Function

const doSomething = something => {
  setTimeout(() => {
    something()
    doSomething(something)
  }, 3600000 - new Date().getTime() % 3600000)
}

Usage

// Do something at next full hour and repeat forever
doSomething(() => console.log('Full hour reached!'))

---------- OLD ANSWER WITH MORE CONTROL ----------

Function

const doSomething = (something) => {
  let running = true
  let nextHour = () => {
    return 3600000 - new Date().getTime() % 3600000
  }
  let nextCall = setTimeout(() => {
    something()
    doSomething(something)
  }, nextHour())
  return {
    next() { return running ? nextHour() : -1 },
    exec() { something() },
    stop() {
      clearTimeout(nextCall)
      running = false
    },
    start() {
      clearTimeout(nextCall)
      nextCall = setTimeout(() => {
        something()
        doSomething(something)
      }, nextHour())
      running = true
    }
  }
}

Usage

// Do something at next full hour and repeat forever
doSomething(() => console.log('Full hour reached!'))

// Do something every full hour & stop it
let obj = doSomething(() => console.log('Will I ever execute? :/'))
obj.next() // Time to next execution in milliseconds
obj.next() / 1000 // Time to next execution in seconds
obj.next() / 1000 / 60 // Time to next execution in minutes
obj.stop() // Stop executing every full hour
obj.start() // Continue executing every hour
obj.exec() // Execute now
Twitt answered 8/11, 2019 at 13:36 Comment(1)
Higher requirement/lower compatibility and not contained in a nice clean callable/named function?Dirham
W
6

High performance & Short answer:

const runEveryFullHours = (callbackFn) => {
  const Hour = 60 * 60 * 1000;
  const currentDate = new Date();
  const firstCall =  Hour - (currentDate.getMinutes() * 60 + currentDate.getSeconds()) * 1000 - currentDate.getMilliseconds();
  setTimeout(() => {
    callbackFn();
    setInterval(callbackFn, Hour);
  }, firstCall);
};

Usage:

runEveryFullHours(() => console.log('Run Every Full Hours.'));
Warrigal answered 10/12, 2020 at 10:8 Comment(0)
P
4

This is how I would go about it, expanding on the previous answer:

function callEveryFullHour(my_function) {

    var now = new Date();
    var nextHour = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0);
    var difference = nextHour - now;

    return window.setTimeout(function(){

        // run it
        my_function();

        // schedule next run
        callEveryFullHour(my_function);

    }, difference);
}

This way you can start stop any function from running on the full hour.

Usage:

let my_function = () => { // do something };
let timeout = callEveryHour(my_function);

// my_function will trigger every hour

window.clearTimeout(timeout);

// my_function will no longer trigger on the hour
Pi answered 29/9, 2017 at 7:15 Comment(0)
M
4

You could use something like the function below, which is very easy to adapt to your needs.

It calculates how long until the next interval, and will then re-run the script after it has been run each time (unless you set the 3rd argument to true).

function runOnInterval(interval_in_ms, function_to_run, only_run_once = false){
    setTimeout(()=>{
        function_to_run();
        if (!only_run_once) runOnInterval(...arguments);
    }, interval_in_ms - ((Date.now() - (new Date().getTimezoneOffset() * 6e4)) % interval_in_ms));
}

Example Usage:

// Every day at midnight:
runOnInterval(24 * 60 * 60 * 1000, my_function_to_run);
// Every hour on the hour:
runOnInterval(60 * 60 * 1000, my_function_to_run);
// Every minute on the minute:
runOnInterval(60000, my_function_to_run);
// Every 10 seconds on the 10 second mark:
runOnInterval(1e4, ()=>console.log('this will be run every 10 seconds on the 10 second mark'));
Mccallum answered 1/7, 2019 at 0:43 Comment(0)
E
3

You could do it by clearing and setting the interval each time. It's just that first time, instead of the interval being one hour, it would be one hour minus the current minutes and seconds:

var d = new Date();
var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
var intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );

function myFn() {
    // do stuff
    // ...
    clearInterval( intervalId );
    intervalId = setInterval( myFn, 60*60*1000 );
}

The only problem with this is that eventually it will probably start drifting...the solution to that would be to just do the same thing inside the function as you do when kicking it off:

var d = new Date();
var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
var intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );

function myFn() {
    // do stuff
    // ...
    clearInterval( intervalId );
    var d = new Date();
    var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
    intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );
}

Here's a proof of concept that updates every minute (I didn't want to wait a whole hour to test my code!): http://jsfiddle.net/dRsua/

Eidetic answered 6/9, 2012 at 22:14 Comment(0)
H
3

Proposing improvement to the approach proposed by Steffan, this one worked best for me.

Function:

let doSomething = function (interval, task) {
      let running = false
      let nextExec = () => {
        if (interval < 0) interval = 1000
        return interval - (new Date().getTime() % interval)
      }
      let taskwrapper = () => {
        //console.log("do more...")
        task()
      }
      let trigger = () => {
        if (nextCall) clearTimeout(nextCall)
        if (running) {
          nextCall = setTimeout(() => {
            taskwrapper()
            trigger()
          }, nextExec())
        }
      }
      let nextCall = null
      return {
        next() {
          return running ? nextExec() : -1
        },
        exec() {
          taskwrapper()
        },
        stop() {
          if (nextCall) clearTimeout(nextCall)
          running = false
        },
        start() {
          if (!running) {
            running = true
            if (nextCall) clearTimeout(nextCall)
            trigger()
          }
        }
      }
    }

/*instantiate an object doSomething(interval, task) */
let obj = doSomething(5000, () => console.log('You go..!'))

/*schedule You go...! every 5 seconds */
obj.start()

/*stop printing  */
//obj.stop()

/*execute task once */
obj.exec()
Hibben answered 6/10, 2020 at 14:32 Comment(0)
S
2

You need to run a setInterval function every minute (or every second depending on how accurate you want your timer to be) and execute your code when minutes are zero (btw get minutes returns a number between 0 and 59)..

 function myTimer() {
        var d = new Date()
        if (d.getMinutes() == 0) {
        console.log("full hour");
        }
}

 timer = setInterval(function(){myTimer()},60000)

If you do not want to have an interval running every second/minute after you established that you are on full hour you can simply trigger a new hourly interval and clear the initial one.

var myHourlyTimer = null;

     function myTimer() {
            var d = new Date()
            if (d.getMinutes() == 0) {
            console.log("full hour");
myHourlyTimer = setInterval(function(){myHourlyTimerFunction()},3600000);
clearInterval(timer)
            }
    }

 timer = setInterval(function(){myTimer()},60000)
Stitt answered 6/9, 2012 at 22:13 Comment(0)
S
1

Was also looking for this, based on Mark's response, I wrotethis:

function callEveryFullHour() {

    var now = new Date();
    var nextHour = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0);
    var difference = nextHour - now;

    window.setTimeout(function(){

        // code goes here

        console.log("It's a full hour!")
        callEveryFullHour();

    }, difference);

}
Spermatozoon answered 18/9, 2015 at 5:13 Comment(0)
H
1
let timerId = null

function wrapper (cb,date = new Date()) {
    if(!date.getMinutes() && !date.getSeconds()){
        setInterval(cb,60*60*1000);
        clearInterval(timerId)
    }
}

timerId = setInterval(wrapper,1000,yourCb)

use a wrapper function that will get executed every second till the full hour has arrived and then will call setInterval(cb,60*60*1000) and then will stop executing.

Hendecahedron answered 8/12, 2020 at 19:0 Comment(0)
Y
1

Using Cron Job would be suitable instead of pure js implementation.

var CronJob = require('cron').CronJob;
var job = new CronJob('* * * * * *', function() {
  console.log('You will see this message every second');
}, "@hourly", true, 'America/Los_Angeles');
job.start();
Yoheaveho answered 9/12, 2020 at 10:55 Comment(0)
W
1

If this script is running at a Unix-like server, not a browser, then crontab might be the best solution than making the schedule hourly in the script.

https://www.geeksforgeeks.org/crontab-in-linux-with-examples/

Wold answered 10/12, 2020 at 6:39 Comment(0)
N
0

One simple way would be to continously run a check to detect when the hour changes:

var lastProcessedHour = -1;

setInterval(function() {
   var d = new Date();
   var currentHour = d.getHours();
   if (currentHour != lastProcessedHour) {
      // do stuff
      console.log("new hour");

      lastProcessedHour = currentHour;
   }
}, 1000);

If you run it every second like above the script will trigger one second into the new hour at the latest.

I think this method is both robust and easy to understand, and from a performance standpoint it should't really be an issue to run this simple check once every second.

Nourishment answered 6/9, 2012 at 22:28 Comment(0)
E
0
  1. Create a job.
  2. Schedule to run it on the server.
Enormous answered 9/12, 2020 at 17:17 Comment(0)
T
0

Basically the same as many others, but a little cleaner. Other fail to note that Date().getTime() is integer multiple of 3600000 on the top of every hour.

fn();          // call this once to exec on the hour

function fn(now) {

   //############## add this to top of your existing fn ###############
   var to_top = 3600000 - new Date().getTime()%3600000;  // msec to top of hour

   clearTimeout(fn.t);                           // in case called out of turn
   fn.t = setTimeout(fn, to_top, true);

   if( !now ) return;
   //###################################################################

   ...fn stuff...
}
Twowheeler answered 24/12, 2021 at 11:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.