Reputation: 401
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
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
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
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
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
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
Reputation: 80
Upvotes: 0
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
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
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
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
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
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
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
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
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
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
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