Context
I m actually developping an application in which people have tasks to make in a day. It's just like "wash the window, clean the floor" etc.. etc...
Each task has a recurrence, and needs to be done at least one time in this period.
Example :
Task 1 has to be done every 15 days. Actually, it's been 16 days that it has not been done. So an event should be emitted.
My client needs to be alerted when something hasn't been done , on a time event, with a proper manner
I know that cron tasks exist for this, I could run one at midnight everyday, and send a mail alert with this. But what if I need to be exactly 15 days ? seconds by seconds ?
I really need efficiency without running this kind of scripts every days.
Does it exist a better way ?
The simplest way of working with timed events in Javascript is through setInterval.
You should keep in mind that the interval size parameter of the function is a 32-bit integer, which limits its size to 2147483647.
This parameter is given in miliseconds, so your maximum interval is about 24 days (2147483647 / (1000 [ms in a sec] * 3600 [secs in an hour] * 24 [hours in a day]).
All you have to do is:
setInterval(function(){
doWhateverYouNeedHere();
}, interval);
Edit
You could workaround the interval size parameter by stacking recursive setTimeout calls with a lower interval:
setTimeout(function(){
setTimeout(function(){
doWhateverYouNeedHere();
setTimeout(function(){
setTimeout(function(){
doWhateverYouNeedHere();
}, not_so_big_interval);
}, not_so_big_interval);
}, not_so_big_interval); // max 24 days
}, not_so_big_interval); // max 24 days
// In total, your function will run every 48 days.
Related
I'm trying to create a stopwatch in Javascript which is slower than usual. Where 1 second takes 30 real life seconds to complete.
No start/stop buttons are required, the code just needs to display a timer that starts from 0 years 0 days 0 hours 0 minutes 0 seconds and goes up at a custom, modifiable speed. How would this be done?
use setInterval (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) to update a variable that is the current time on the watch as a variable. setInterval takes in a delay, so you can custom configure that to change the speed of the timer. Whenever someone changes the speed you need to kill the last setInterval with clearInterval() and pass in the reference to the running interval.
Read a bit of that documentation and you should be good to go.
agree with other answer, setInterval is the most suitable method, here is an example which starts from the current date/time and moves at a slower rate of 30 real seconds = 1 program second:
var tcount = 1;
var step = 30000;
var d = new Date();
console.log(d.toDateString()+' '+d.toLocaleTimeString());
var timr = setInterval(()=> {
d.setSeconds(d.getSeconds()+tcount);
console.log(d.toDateString()+' '+d.toLocaleTimeString());
}, step);
The code I wrote to call a function on the minute every minute, I think is flawed, as It's good for a while, but tends to lag behind by about 15 seconds for every hour since the page was loaded. To be honest I can't figure out what's causing the lagging, maybe it's the time it takes the functions to execute, small lapses all adding up and accumulating. Is there a way to auto-correct the lapses within the function as it's called. Or maybe someone knows a better method of achieving on the minute function calls. Any help or ideas much appreciated. Thanks.
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function setTimer() {
onTheMinFunc();
setTimeout(setTimer, delay);
}, start);
First of all, the DOM Timers API does not guarantee accuracy. I quote:
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
Second, you have a lag on each round caused by the time onTheMinFunc() is executed (you only set the timeout when it's done).
So, let's say onTheMinFunc takes half a second to execute - you get half a second delay at each minute and it accumulates - after only 10 minutes it'll lag quite a bit. (Note, functions should usually not take more than 15ms to execute anyway to avoid noticeable lag)
Try:
setInterval(onTheMinFunc, delay);
It still won't be very accurate. You can poll on much shorter intervals and keep track of a date variable - but again - no guarantees.
What you probably want is setInterval:
setInterval(onTheMinFunc, delay);
As is, your code using setTimeout means that the time it takes to execute your onTheMinFunc is being added into your delay before the next one is started, so over time, this extra delay will add up.
Using setInterval will be more accurate, since the delay is between calls to execute the function, rather than starting the timer only after the function is finished.
Timers and javascript times aren't very accurate, and I would think the only way to make sure a function is executed every whole minute over time, is to check the seconds every second
setInterval(function() {
if ( new Date().getSeconds() === 0 ) onTheMinFunc();
},1000);
FIDDLE
Here is a slight modification to your code:
function everyMinute(fn) {
arguments[1] && fn();
var now = new Date();
var delay = 60 * 1000 - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function(){
everyMinute(fn, true);
}, start);
}
everyMinute(onTheMinFunc);
It recalculates the number of milliseconds to wait till the next minute every time so it is as accurate as possible to the top of the minute.
I think you want something closer to this:
function setNextMinute() {
// figure out how much time remains before the end of the current minute
var d = new Date().getTime()%60000;
//set a timeout to occur when that expires.
setTimeout(function () {
// recalculate a new timeout so that your timer doesn't lag over time.
doWhateverYouWantToHere();
// note that calling doWhateverYouWantToHere() will
// not offset the next minute, since it is recalculated in setNextMinute()
setNextMinute();
},60000-d);
}
setNextMinute();
caveat: I did not thoroughly test this for timing. But it appeared to work for 1 sec intervals and 1 min intervals well enough.
This has the advantage of not recalculating every second, and also not just starting a 60 second timer from whatever the current time is.
The current accepted answer may overkill
Executing if ( new Date().getSeconds() === 0 ) onTheMinFunc(); on each second (and forever) seems to not be a good idea.
I will not benchmark it against the following propositions, it's not necessary.
Clues
Use whatever logic is necessary to calculate the start moment.
On the start moment
Use setInterval for remaning executions
Execute the first call
Note setInterval is called ASAP to avoid that time lapses.
If you want that new Date().getSeconds() === 0:
var id = setInterval(function() {
if ( new Date().getSeconds() === 0 ) {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
clearInterval(id);
}
},1000);
Alternatively, you could use your own logic:
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function() {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
}, start);
Please check both examples working on jsfiddle
The second (Example B) seems more accurate.
I'm trying to make a countdown that is counting down in milliseconds; however, the countdown actually takes much longer than 7 seconds. Any idea as to why?
function countDown(time){
var i = 0;
var interval = setInterval(function(){
i++;
if(i > time){
clearInterval(interval);
}else{
//mining
$('#mining_time').text($('#mining_time').text()-1);
}
}, 1);
}
And I can confirm the varible time passed to the function is correctly set to 7000.
For a mostly-accurate countdown, use setTimeout().
setTimeout(fn, 7e3);
If you absolutely must have it as close to 7 seconds as possible, use a tight poll (requestAnimationFrame()) and look at difference between the time of start and current poll.
var startTime = Date.now();
requestAnimationFrame(function me() {
var deltaTime = Date.now() - startTime;
if (deltaTime >= 7e3) {
fn();
} else {
requestAnimationFrame(me);
}
});
Poly-fill as required.
the most precise way to run something after 7 seconds - is to use setTimeout with 7000 ms interval
a. there is no browser that guarantees an interval to run with 1ms resolution. In the best case it would be 7-10ms
b. there is only one thread in js, so the tasks are queued. It means that the next run will be scheduled to only after the current run is finished.
Some useful reading: http://ejohn.org/blog/how-javascript-timers-work/
No browser will take 1 as parameter for setInterval. Off the top of my head the minimum is 4 ms.
For an accurate result, get the current time, add 7000 ms, and poll (using setInterval or setTimeout) until you reach that new time.
A quick Web search returned this article that provides an example.
[Update] the value of 4 ms is mentioned on this MDN page.
My script has a countdown timer, but has to be controlled by the server since the time can change at any time.
Usually counts down from 1 hour, but as it gets closer to 0, a user fired event from PHP will reset the time on MySQL database and the timer will shoot back to what ever time.
setInterval(function() {
$('#timer').load('api/getdata.php?clock=1');
}, 600);
As you can see, currently the countdown is returned by a PHP script, which is called every 600ms (1000ms sometimes appears to skip a second).
I need to have a simple text countdown (Minutes and seconds), which synchronizes with the PHP script. But as it gets less than 30 seconds, would need to sync every second.
Is this possible with JS?
This is the code for the PHP
$from = time();
$to = strtotime($row['clock']);
$Timer = date('i:s', $to - $from);
echo("$Timer");
I think the easiest way to tackle this ( correct me if I'm wrong anyone! );
Fetch the time from the server
Return [ json ]
[server_time]
[seconds_left]
[miliseconds_left]
Set these values on a function and make sure its called as your contract requires. As soon as this starts hitting < 60 seconds shorten the interval by division of (e.g. the amount of seconds, altough this might not be optimal :-) ).
60/60 = 1
50 / 60 = .73
30 / 60 = .5
etc...
If the miliseconds are really really crucial I'm afraid you would have to provide some more insight of what it actually is you are trying to accomplish?
For an upcoming project with node.js I need to perform various housekeeping tasks at periodic times. Specifically some tasks every millisecond, others every 20 ms (50 times per second) and still others every second. So I thought about using setInterval(), with funny results: many function calls were being skipped.
The benchmark I used is as follows:
var counter = 0;
var seconds = 0;
var short = 1;
setInterval(function() {
counter ++;
}, short);
setInterval(function() {
seconds ++;
log('Seconds: ' + seconds + ', counter: ' +
counter + ', missed ' +
(seconds * 1000 / short - counter));
}, 1000);
There is a long timer of one second and a short one that can be adjusted using the variable short, in this case 1 ms. Every second we print the difference between the number of expected ticks in the short cycle and the actual number of times the short counter was updated.
Here is how it behaves when the short timer is 1 ms:
2012-09-14T23:03:32.780Z Seconds: 1, counter: 869, missed 131
2012-09-14T23:03:33.780Z Seconds: 2, counter: 1803, missed 197
2012-09-14T23:03:34.781Z Seconds: 3, counter: 2736, missed 264
...
2012-09-14T23:03:41.783Z Seconds: 10, counter: 9267, missed 733
Many function calls are skipped. Here it is for 10 ms:
2012-09-14T23:01:56.363Z Seconds: 1, counter: 93, missed 7
2012-09-14T23:01:57.363Z Seconds: 2, counter: 192, missed 8
2012-09-14T23:01:58.364Z Seconds: 3, counter: 291, missed 9
...
2012-09-14T23:02:05.364Z Seconds: 10, counter: 986, missed 14
Better, but roughly one function call is skipped every second. And for 20 ms:
2012-09-14T23:07:18.713Z Seconds: 1, counter: 46, missed 4
2012-09-14T23:07:19.713Z Seconds: 2, counter: 96, missed 4
2012-09-14T23:07:20.712Z Seconds: 3, counter: 146, missed 4
...
2012-09-14T23:07:27.714Z Seconds: 10, counter: 495, missed 5
Finally for 100 ms:
2012-09-14T23:04:25.804Z Seconds: 1, counter: 9, missed 1
2012-09-14T23:04:26.803Z Seconds: 2, counter: 19, missed 1
2012-09-14T23:04:27.804Z Seconds: 3, counter: 29, missed 1
...
2012-09-14T23:04:34.805Z Seconds: 10, counter: 99, missed 1
In this case it skips very few calls (the gap increased to 2 after 33 seconds and to 3 after 108 seconds.
The numbers vary, but are surprisingly consistent between runs: Running the first 1 ms benchmark three times yielded a delay after 10 seconds of 9267, 9259 and 9253.
I have found no references for this particular problem. There is this much cited Ressig post and lots of related JavaScript questions, but most assume that the code runs in a browser and not in node.js.
Now for the dreaded question: what is going on here? Just joking; obviously function calls are being skipped. But I fail to see the pattern. I thought that the long cycles might be preventing the short ones, but it doesn't make any sense in the 1 ms case. Short cycle function calls are not overlapping since they just update a variable, and the node.js process is near 5% CPU even with a short cycle of 1 ms. Load average is high though, at about 0.50. I don't know why a thousand calls are stressing my system so much, though, since node.js handles many more clients perfectly; it must be true that setInterval() is CPU intensive (or I am doing something wrong).
An obvious solution is to group function calls using longer timers, and then run short cycle function calls many times to simulate a shorter timer. Then use the long cycle as a "broom wagon" that makes any calls missed in the lower intervals. An example: set up 20 ms and 1000 ms setInterval() calls. For 1 ms calls: call them 20 times in the 20 ms callback. For the 1000 ms call: check how many times the 20ms function has been called (e.g. 47), do any remaining calls (e.g. 3). But this scheme is going to be a bit complex, since calls may overlap in interesting ways; also it will not be regular although it may look like it.
The real question is: can it be done better, either with setInterval() or other timers within node.js? Thanks in advance.
SetInterval functions in javascript are not accurate. You should try to use a high resolution timer.Building accurate Timers in javascript
Look at this doc: http://nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg
It is important to note that your callback will probably not be called in exactly delay milliseconds - Node.js makes no guarantees about the exact timing of when the callback will fire, nor of the ordering things will fire in. The callback will be called as close as possible to the time specified.
This happens because application code blocks the event loop. All timers and I/O events can be handled only on the nextTick.
You can see this behaviour with this code:
setInterval(function() {
console.log(Date.now());
for (var i = 0; i < 100000000; i++) {
}
}, 1);
Try to change iterations count and see results.
Ideally, the timer will be triggered exactly if the applications tick will last less than one ms. But this is not practicable in a real application.
The answer happens to be a combination of those given by Vadim and zer02, so I am leaving a write-up here. As Vadim said, the system cannot cope with too frequent updates, and adding some load to the system is not going to help. Or rather the runtime cannot cope; the system should be more than capable of firing the callback every millisecond if needed, but for some unexplained reason often it doesn't want to.
The solution is to use accurate timers, as zer02 commented. Do not be misled by the name; the mechanism used is the same setTimeout(), but the delay is adjusted depending on the time left until the timer should fire. So, if the time is over then the "accurate timer" will call setTimeout(callback, 0) which is run immediately. System load is, surprisingly, less than with setInterval(): about 2% of the CPU instead of 5%, in my very unscientific sample.
This simple function may come in handy:
/**
* A high resolution timer.
*/
function timer(delay, callback)
{
// self-reference
var self = this;
// attributes
var counter = 0;
self.running = true;
var start = new Date().getTime();
/**
* Delayed running of the callback.
*/
function delayed()
{
callback(delay);
counter ++;
var diff = (new Date().getTime() - start) - counter * delay;
if (!self.running) return;
setTimeout(delayed, delay - diff);
}
// start timer
delayed();
setTimeout(delayed, delay);
}
To use, just call new timer(delay, callback);. (Yes, I reversed the order of the parameters since having the callback first is very annoying.) To stop it, set timer.running = false.
One final note: setTimeout(callback, delay) does not use recursion as I feared (as in: wait for some time, then invoke the callback), it just places the callback in a queue which will be called by the runtime when its turn comes, in the global context.
I Disabled Debugger and tried again.It worked fine for me