matlos
matlos

Reputation: 401

JavaScript: How to do something every full hour?

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

Upvotes: 20

Views: 31993

Answers (17)

Steffan
Steffan

Reputation: 733

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

Upvotes: 6

codechimp
codechimp

Reputation: 1557

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...
}

Upvotes: 0

Masih Jahangiri
Masih Jahangiri

Reputation: 10957

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.'));

Upvotes: 6

Alan Omar
Alan Omar

Reputation: 4227

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.

Upvotes: 1

Pengson
Pengson

Reputation: 875

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/

Upvotes: 1

ngprnk
ngprnk

Reputation: 92

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

Upvotes: 1

Heretic Monkey
Heretic Monkey

Reputation: 12114

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.

Upvotes: 34

Shuja Jamil
Shuja Jamil

Reputation: 91

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

Upvotes: 3

RedSparr0w
RedSparr0w

Reputation: 468

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

Upvotes: 4

Luc Bocahut
Luc Bocahut

Reputation: 41

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

Upvotes: 4

sigmaxf
sigmaxf

Reputation: 8492

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

}

Upvotes: 1

Michal
Michal

Reputation: 13649

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)

Upvotes: 2

Ethan Brown
Ethan Brown

Reputation: 27282

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/

Upvotes: 3

Strille
Strille

Reputation: 5781

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.

Upvotes: 0

Rami Jarrar
Rami Jarrar

Reputation: 4643

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

Upvotes: 11

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324750

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

Upvotes: 11

Related Questions