setInterval with exact timeout - javascript

Is there some way to make a function just like the setInterval but the timeout to be exactly the same each time.
In setInterval the timeout varies about the given timeout, a little bit more, a little bit less, but very rare exactly the same.
For example:
var before = new Date().getTime();
setInterval(function() {
var after = new Date().getTime();
newTab.window.location=someURL;
console.log((after - before));
before = after;
}, 50000);
prints 50000,50002, 50005, 50994, 50997, 49999, 50003, 49998 and so on.
I want to be printed always 50000

Javascript is executed in one flow only, so if there is another process doing something at the same time, there's always a chance you timer function will not be executed in time.
If you really need the exact time interval you can block the execution of any other process in advance and hope for the best:
function setExactInterval(handler, time) {
var startTime = Date.now();
setTimeout(function() {
while (true) {
var currentTime = Date.now();
var diff = currentTime - startTime;
if (diff >= time) {
setExactInterval(handler, time);
return handler();
}
}
}, time - 50);
}
It still will not be exact in case the process is blocked by OS though...

Related

How to make the page go back to the beginning after a certain time? [duplicate]

This question already has answers here:
How to detect idle time in JavaScript
(37 answers)
Closed 10 months ago.
I'm creating a webpage and I decided to try to get you back to the start automatically if it takes a certain amount of time without clicking the screen. I tried to do so but when I enter the page no matter how much it touches the screen the time does not reset and the alert appears anyway. The webpage is also loading and does not load all the content. Here is the code I currently have:
var startTime = new Date();
document.addEventListener('DOMContentLoaded', function(){
var seconds = 1;
while (seconds < 5) {
endTime = new Date();
var timeDiff = endTime - startTime; //in ms
// strip the ms
timeDiff /= 1000;
// get seconds
var seconds = Math.round(timeDiff);
console.log(seconds + " seconds");
setTimeout(() => {}, 1000); // Like time.sleep
}
alert("Touch the screen!")
})
document.addEventListener("click", function() {
console.log("Hello")
startTime = new Date()
});
Thank you!
This will check every 5 seconds if any activity has occurred, if not then trigger your page update.
var timestamp = Date.now();
var checkTime = null;
document.addEventListener('DOMContentLoaded', function(){
checkTime = setInterval(doTimeout, 5000);
});
document.addEventListener("click", function() {
console.log("Hello")
timestamp = Date.now();
});
function doTimeout(){
if (Date.now() - timestamp >= 5000){
console.log("change page");
}
}
The setTimeout function is an async function, so is not block the main thread.
You should use
let startTime=new Date();
setInterval(function() {
let currentTime=new Date();
let timeDiff=(currentTime-startTime)/1000;
if(timeDiff>5) {/*code to scroll up the page*/}
},200) //it checks every 200 millisecodns the time
document.addEventListener("click", function() {
console.log("Hello")
startTime = new Date()
});
The "DOMContentLoaded" is fired once the HTML document is loaded, this often means that it is before css and images are loaded to the page, which could explain why your page isn't fully loading. I don't see any logic which is resetting the timer once the page is clicked, you should have an on click event listener which will reset the timer, and move the timer variable outside of a level of scope so that both event listeners can use it.
I think you have a misconception about how setTimeout works.
With setTimeout you can register a function that will be called after the indicated number of ms has passed.
The function you give it is a noop, so I'm guessing you imagine setTimeout to have a side effect. In this situation particularly, I'm guessing you are expecting setTimeout to halt the execution of the loop, but that isn't the case. If you are expecting it to work like a time.sleep function, thats not how it works, since after registering the callback, it simply moves on.
So the page isn't fully loading, because it has to continously run this while loop, and can't do anything else until it's done with that.
To get something more similar to time.sleep you can use async/await.
function sleep(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
document.addEventListener('DOMContentLoaded', async function(){
var seconds = 1;
while (seconds < 5) {
endTime = new Date();
var timeDiff = endTime - startTime; //in ms
// strip the ms
timeDiff /= 1000;
var seconds = Math.round(timeDiff);
console.log(seconds + " seconds");
await sleep(1000)
}
alert("Touch the screen!")
})
In the above I defined a helper function, sleep, which creates a Promise, that simply resolves after the indicated amount of ms has passed.
To use this, the function using it has to be marked as async. That's why you can see the async keyword right before the function keyword in the addEventListener call.
Once that requirement is fulfilled, you can use the sleep function by adding the await keyword in front of it.
call setTimeout once then call clearTimeout if there are touch or click event
document.addEventListener('DOMContentLoaded', function() {
var timeout = setTimeout(function() {
console.log('page will go back!');
}, 3000);
["click", "touchstart"].forEach(function(event) {
document.addEventListener(event, function() {
clearTimeout(timeout)
console.log('will not go back')
});
})
});

Javascript - Pausing an IIFE output that has setTimeout and displays min/sec - Pomodoro Clock

I have two functions that display minutes and seconds. Inside the functions I'm using an IIFE with a setTimeout to calculate the time. After getting this, having a hard time figuring out how I could pause the display if pause button is clicked.
The timer works fine and displays correctly.
I realize this is probably not a good way to do it, but I spent so much time (trying to learn how to use IIFE) that I don't want to give up. If I have to, then I will scratch it.
Update - This timer will be getting input from the user. It might be 25 minutes. I want it to start counting down from there until it reaches 0, and the user able to pause at anytime.
let convertToSeconds = document.getElementById('timeValue').value * 60;
let seconds = 60;
function secondsCounter(time) {
// let flag = document.getElementById('pauseButton').value;
for (let i = seconds; i > 0; i--) {
(function(x) {
setTimeout(function() {
let remaining = seconds - x;
document.getElementById('displaySeconds').innerHTML = remaining;
console.log(remaining);
}, i * 1000);
})(i);
}
}
function counter() {
for (let i = convertToSeconds; i > 0; i--) {
(function(minutes) {
setTimeout(function() {
let remaining = Math.floor((convertToSeconds - minutes) / 60);
document.getElementById('displayMinutes').innerHTML = remaining;
console.log(remaining);
}, i * 1000);
setTimeout(function() {
secondsCounter(seconds);
}, i * 60000);
})(i);
}
secondsCounter(seconds);
}
I've tried a couple of things.
Using a flag and if statement around document.getElementById('displaySeconds').innerHTML = remaining; so if my pause button is clicked, the flag changes, and another setTimeout (10 minutes) is triggered. Doesn't stop the countdown on the DOM, it keeps going. I just wanted to see some reaction, but nothing happened. Something like:
function secondsCounter(time) {
let flag = document.getElementById('pauseButton').value;
for (let i = seconds; i > 0; i--) {
(function(x) {
setTimeout(function() {
let remaining = seconds - x;
if (flag === 'yes') {
document.getElementById('displaySeconds').innerHTML = remaining;
console.log(remaining);
} else {
setTimeout(function() {
console.log(remaining);
}, 10000);
}
}, i * 1000);
})(i);
}
}
Using a setInterval and clearInterval that didn't do anything.
Is this possible? Not sure where else to look. Thank you
You can't stop/pause a setTimeout or clearTimeout without making a reference to the timer, storing it and then calling clearTimeout(timer) or clearInterval(timer).
So, instead of: setTimeout(someFunciton)
You need: timer = setTimeout(someFunciton)
And, the timer variable needs to be declared in a scope that is accessible to all functions that will use it.
See setTimeout() for details.
Without a reference to the timer, you will not be able to stop it and that's caused you to go on a wild goose chase for other ways to do it, which is overthinking what you actually need.
In the end, I think you should just have one function that does all the counting down so that you only have one timer to worry about.
Lastly, you can use the JavaScript Date object and its get / set Hours, Minutes and Seconds methods to take care of the reverse counting for you.
(function() {
// Ask user for a time to start counting down from.
var countdown = prompt("How much time do you want to put on the clock? (hh:mm:ss)");
// Take that string and split it into the HH, MM and SS stored in an array
var countdownArray = countdown.split(":")
// Extract the individual pieces of the array and convert to numbers:
var hh = parseInt(countdownArray[0],10);
var mm = parseInt(countdownArray[1],10);
var ss = parseInt(countdownArray[2],10);
// Make a new date and set it to the countdown value
var countdownTime = new Date();
countdownTime.setHours(hh, mm, ss);
// DOM object variables
var clock = null, btnStart = null, btnStop = null;
// Make a reference to the timer that will represent the running clock function
var timer = null;
window.addEventListener("DOMContentLoaded", function () {
// Make a cache variable to the DOM element we'll want to use more than once:
clock = document.getElementById("clock");
btnStart = document.getElementById("btnStart");
btnStop = document.getElementById("btnStop");
// Wire up the buttons
btnStart.addEventListener("click", startClock);
btnStop.addEventListener("click", stopClock);
// Start the clock
startClock();
});
function startClock() {
// Make sure to stop any previously running timers so that only
// one will ever be running
clearTimeout(timer);
// Get the current time and display
console.log(countdownTime.getSeconds());
countdownTime.setSeconds(countdownTime.getSeconds() - 1);
clock.innerHTML = countdownTime.toLocaleTimeString().replace(" AM", "");
// Have this function call itself, recursively every 900ms
timer = setTimeout(startClock, 900);
}
function stopClock() {
// Have this function call itself, recursively every 900ms
clearTimeout(timer);
}
}());
<div id="clock"></div>
<button id="btnStart">Start Clock</button>
<button id="btnStop">Stop Clock</button>

Is there an elegant way to implement a clock-update event?

As far as I know (and I know relatively little), there is no native event that is emitted when e.g. the seconds hand ticks. The best I came up with is repeatedly checking a Date object (e.g. every 333ms; a shorter interval results in higher precision but is also more resource intensive). Somehow, if I use the same Date object over and over, time won't update, while
Date.prototype.getSeconds()
logs 'NaN' although typeof is 'number'.
function clock(interval) {
var d = new Date();
var secondsOld = d.getSeconds();
d = null;
setInterval(function() {
var d = new Date();
var secondsNew = d.getSeconds();
if ( secondsNew !== secondsOld ) {
secondsOld = secondsNew;
// trigger something
console.log(secondsOld);
}
d = null;
}, interval);
}
You're right in that there are no native clock tick events. The most performant while precise way I would approach this issue is using setTimeout() and Date.now().
Create a recursive function that calls setTimeout() every second. To be precise, make the timeout the duration until the next exact second from that function call. Here's an example.
// milliseconds per second
var SECOND = 1000;
function bindClockTick(callback) {
function tick() {
var now = Date.now();
callback(now);
setTimeout(tick, SECOND - (now % SECOND));
}
tick();
}
bindClockTick(function(ms) {
console.log('tick! milliseconds: '+ ms);
});
This uses Date.now() instead of creating a new instance of the Date class.
Here's a JSFiddle to test the precision. The demo uses new Date() to easily display the current time, but it can be done with just milliseconds.
I'm not sure why you would want to sync with the exact second change; however, here's how I would do it:
function clock() {
var startMs = Date.now(),
startSecs = Math.floor(startMs / 1000),
firstOffset = 1000 - startMs % 1000;
function tick () {
var ms = Date.now(),
secs = Math.floor(ms / 1000),
dSecs = secs - startSecs;
console.log(dSecs);
}
setTimeout(function () {
tick();
setInterval(tick, 1000);
}, firstOffset);
tick();
}
clock();
Here's what happens:
I grab the current time in ms with Date.now()
I figure out how many ms until the next second tick (firstOffset)
I set the initial setTimeout to this offset, making sure that it will fire on the next second tick.
Now that we are synced with the second tick, setInterval with 1000 ms will continue to update every second.

Accurately run a function when the minute changes?

How could I accurately run a function when the minute changes? Using a setInterval could work if I trigger it right when the minute changes. But I'm worried setInterval could get disrupted by the event-loop in a long-running process and not stay in sync with the clock.
How can I run a function accurately when the minute changes?
First off, you should use setInterval for repeating timers, since it (tries to) guarantee periodic execution, i.e. any potential delays will not stack up as they will with repeated setTimeout calls. This will execute your function every minute:
var ONE_MINUTE = 60 * 1000;
function showTime() {
console.log(new Date());
}
setInterval(showTime, ONE_MINUTE);
Now, what we need to do is to start this at the exact right time:
function repeatEvery(func, interval) {
// Check current time and calculate the delay until next interval
var now = new Date(),
delay = interval - now % interval;
function start() {
// Execute function now...
func();
// ... and every interval
setInterval(func, interval);
}
// Delay execution until it's an even interval
setTimeout(start, delay);
}
repeatEvery(showTime, ONE_MINUTE);
This may be an idea. The maximum deviation should be 1 second. If you want it to be more precise, lower the milliseconds of setTimeout1.
setTimeout(checkMinutes,1000);
function checkMinutes(){
var now = new Date().getMinutes();
if (now > checkMinutes.prevTime){
// do something
console.log('nextminute arrived');
}
checkMinutes.prevTime = now;
setTimeout(checkChange,1000);
}
1 But, see also this question, about accuracy of timeouts in javascript
You can try to be as accurate as you can, setting a timeout each X milliseconds and check if the minute has passed and how much time has passed since the last invocation of the function, but that's about it.
You cannot be 100% sure that your function will trigger exactly after 1 minute, because there might be something blocking the event-loop then.
If it's something vital, I suggest using a cronjob or a separate Node.js process specifically for that (so you can make sure the event loop isn't blocked).
Resources:
http://www.sitepoint.com/creating-accurate-timers-in-javascript/
I've put up a possible solution for you:
/* Usage:
*
* coolerInterval( func, interval, triggerOnceEvery);
*
* - func : the function to trigger
* - interval : interval that will adjust itself overtime checking the clock time
* - triggerOnceEvery : trigger your function once after X adjustments (default to 1)
*/
var coolerInterval = function(func, interval, triggerOnceEvery) {
var startTime = new Date().getTime(),
nextTick = startTime,
count = 0;
triggerOnceEvery = triggerOnceEvery || 1;
var internalInterval = function() {
nextTick += interval;
count++;
if(count == triggerOnceEvery) {
func();
count = 0;
}
setTimeout(internalInterval, nextTick - new Date().getTime());
};
internalInterval();
};
The following is a sample usage that prints the timestamp once every minute, but the time drift is adjusted every second
coolerInterval(function() {
console.log( new Date().getTime() );
}, 1000, 60);
It's not perfect, but should be reliable enough.
Consider that the user could switch the tab on the browser, or your code could have some other blocking tasks running on the page, so a browser solution will never be perfect, it's up to you (and your requirements) to decide if it's reliable enough or not.
Tested in browser and node.js
sleeps until 2 seconds before minute change then waits for change
you can remove logging as it gets pretty cluttered in log otherwise
function onMinute(cb,init) {
if (typeof cb === 'function') {
var start_time=new Date(),timeslice = start_time.toString(),timeslices = timeslice.split(":"),start_minute=timeslices[1],last_minute=start_minute;
var seconds = 60 - Number(timeslices[2].substr(0,2));
var timer_id;
var spin = function (){
console.log("awake:ready..set..");
var spin_id = setInterval (function () {
var time=new Date(),timeslice = time.toString(),timeslices = timeslice.split(":"),minute=timeslices[1];
if (last_minute!==minute) {
console.log("go!");
clearInterval(spin_id);
last_minute=minute;
cb(timeslice.split(" ")[4],Number(minute),time,timeslice);
console.log("snoozing..");
setTimeout(spin,58000);
}
},100);
};
setTimeout(spin,(seconds-2)*1000);
if (init) {
cb(timeslice.split(" ")[4],Number(start_minute),start_time,timeslice,seconds);
}
}
}
onMinute(function (timestr,minute,time,timetext,seconds) {
if (seconds!==undefined) {
console.log("started waiting for minute changes at",timestr,seconds,"seconds till first epoch");
} else {
console.log("it's",timestr,"and all is well");
}
},true);
My first thought would be to use the Date object to get the current time. This would allow you to set your set interval on the minute with some simple math. Then since your worried about it getting off, every 5-10 min or whatever you think is appropriate, you could recheck the time using a new date object and readjust your set interval accordingly.
This is just my first thought though in the morning I can put up some code(its like 2am here).
This is a fairly straightforward solution ... the interval for the timeout is adjusted each time it's called so it doesn't drift, with a little 50ms safety in case it fires early.
function onTheMinute(callback) {
const remaining = 60000 - (Date.now() % 60000);
setTimeout(() => {
callback.call(null);
onTheMinute(callback);
}, remaining + (remaining < 50 ? 60000 : 0));
}
Here's yet another solution based on #Linus' post and #Brad's comment. The only difference is it's not working by calling the parent function recursively, but instead is just a combination of setInterval() and setTimeout():
function callEveryInterval(callback, callInterval){
// Initiate the callback function to be called every
// *callInterval* milliseconds.
setInterval(interval => {
// We don't know when exactly the program is going to starts
// running, initialize the setInterval() function and, from
// thereon, keep calling the callback function. So there's almost
// surely going to be an offset between the host's system
// clock's minute change and the setInterval()'s ticks.
// The *delay* variable defines the necessary delay for the
// actual callback via setTimeout().
let delay = interval - new Date()%interval
setTimeout(() => callback(), delay)
}, callInterval, callInterval)
}
Small, maybe interesting fact: the callback function only begins executing on the minute change after next.
The solution proposed by #Linus with setInterval is in general correct, but it will work only as long as between two minutes there are exactly 60 seconds. This seemingly obvious assumption breaks down in the presence of a leap second or, probably more frequently, if the code runs on a laptop that get suspended for a number of seconds.
If you need to handle such cases it is best to manually call setTimeout adjusting every time the interval. Something like the following should do the job:
function repeatEvery( func, interval ) {
function repeater() {
repeatEvery( func, interval);
func();
}
var now = new Date();
var delay = interval - now % interval;
setTimeout(repeater, delay);
}

Javascript game loop that runs at the same speed?

I have a javascript game that will run really fast on some computers and really slow on others. I've been doing some research and I've found that I need to update my loop based on time, but I can't seem to find any good examples of this for javascript. Can someone point me to the right direction on how to get a game to run at 30fps on any hardware?
Thanks
Normally games work from a Delta Time, that is, the amount of time since the last frame was rendered.
Psuedocode (roughly C#):
DateTime lastFrameTimeStamp = DateTime.Now;
void Draw()
{
TimeSpan timeSinceLastFrame = DateTime.Now.Subtract(lastFrameTimeStamp);
float deltaTime = timeSinceLastFrame.TotalSeconds;
// Do all of your movement and other time-based math based on the deltaTime, Like:
float x = x + (MovementPerSecond * deltaTime);
lastFrameTimeStamp = DateTime.Now;
}
Using a Delta Time prevents all dependency on CPU power or how often frames get drawn.
You can't force a game to run at 30fps if the hardware is unable achieve it. If what it is doing is taking more than 1/30th of a second, you're out of luck.
You can use requestAnimationFrame to let it run as fast as it can though. See here: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
You could have a timer function which you can measure how long you are executing for, then call back to yourself at your ((required interval) - (execution time)), in pseudo code
function timer(){
var timeStart = new Date();
// Your stuff
setTimeout (timer, (1000/30) - (new Date() - timeStart));
}
What you are looking for is a simple implementation of delta timing in JavaScript. Implememting it in JavaScript is a relatively simple task. In fact it's so simple that it can be achieved in less than 25 lines of code (stripping out blank lines and comments):
function DeltaTimer(render, interval) {
var timeout;
var lastTime;
this.start = start;
this.stop = stop;
function start() {
timeout = setTimeout(loop, 0);
lastTime = Date.now();
return lastTime;
}
function stop() {
clearTimeout(timeout);
return lastTime;
}
function loop() {
var thisTime = Date.now();
var deltaTime = thisTime - lastTime;
var delay = Math.max(interval - deltaTime, 0);
timeout = setTimeout(loop, delay);
lastTime = thisTime + delay;
render(thisTime);
}
}
Using it is even more simple. Let's learn by example:
var timer = new DeltaTimer(render, 1000 / 30);
var start = timer.start();
var body = document.body;
var frame = 0;
function render(time) {
time -= start;
body.innerHTML += (frame++) + ". " + time + " ms<br/>";
if (time >= 1000) {
var stop = timer.stop() - start;
body.innerHTML += "<br/>" + stop + " ms (stopped)";
}
}
I think the code is pretty self explanatory. For the live demo click on this link.
Frameworks like EaselJS often have Tickers/Timers that run every x milliseconds.
http://easeljs.com/docs/Ticker.html
A list of JavaScript game frameworks:
https://gist.github.com/768272
The only timing mechanisms available to you in JavaScript are setTimeout and setInterval. However, there is no guarantee on the precision of the timers. In fact, since JavaScript in browsers is single threaded there is no guarantee that your timer will fire when you want it to if there is JS running elsewhere already.
Generally speaking, if you want something to occur regularly at a specified interval, you use setInterval. You have to make sure that your callback does not take longer to run than the duration of the interval, otherwise your next interval will fire late. And if that fires late, chances are the next will as well, and you'll just get further and further behind in your executions.
Many modern browsers do support Web Workers, which is essentially a background JS execution (in addition to the primary UI blocking thread we are all familiar with). I have not used Web Workers yet, so I cannot speak to them with any authority or expertise.
If this were me, I would take the following initial approach (http://jsfiddle.net/Ce3wq/):
var GameTimer = (function () {
var gameTimer = function (opts) {
var self = this;
opts = opts || {};
opts.stepInterval = opts.stepInterval || 30;
var callbacks = {};
var stepInterval= opts.stepInterval; // ms
this.domReady = function () {
setInterval(step, stepInterval);
};
this.registerService = function(callback){
callbacks[callback] = callback;
};
this.removeService = function(){
delete callbacks[callback];
};
var step = function () {
for(var id in callbacks){
callbacks[id]();
}
};
};
return new gameTimer;
})();
var eachTick = function(){
console.log(new Date().getTime());
};
GameTimer.registerService (eachTick);
jQuery(document).ready(GameTimer.domReady);
The only option you have with JavaScript is using setTimeout and setInterval to emulate game loops found in other languages.
You cannot force your game to run at the desired speed in each hardware (even if your game is really low on requisites, there will be always stuttering because of web browsers JavaScript engines), but you can still use Delta Timing to not waste time between each frame.
Here is a simple code I use for my JavaScript games. It's a function that gets called FPS times in a second. In the example is 30 times each second.

Categories

Resources