function to calculate function fps - javascript

Ok so I believe I could best describe the issue through code so here goes
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
//do stuff
}
Okay so to clarify I dont want to add a variable to the actual function clicking I would like to be able to call something like
FPS(clicking) and have the function return a value for example
var fps = FPS(clicking);
then i could display the returned number as such
element.innerHTML = fps
EDIT:
I know with the current code it seems silly but this is just example coding not what I am actually using

This is not very actual since Date.now() also uses time.
function FPS(fn) {
var startTime = Date.now();
fn();
var endTime = Date.now();
return endTime - startTime;
}
function longClick() {
var abc = 0;
for (var i = 0; i < 100000000; i++) {
abc++;
}
}
var fps = FPS(longClick);
console.log((fps / 1000) + ' seconds');
FPS usually refers to Frames Per Second which is the frequency of refreshing screen image.
Pick a more comprehensive name, with keywords like Elapsed, for teammates.

If you want to know "how fast the functions runs" :
/**
* Return the execution time of a function
* #param {Function} fn The function to execute
*/
function FPS(fn) {
var start = new Date().getTime();
fn();
var end = new Date().getTime();
return end - start;
}

You can have FPS return a DOMHighResTimeStamp (works in IE 10+, Firefox 15+, Chrome 20+, Safari 8+) which will return time in milliseconds. If you want it to work in older browser you can replace the precision timing with a Date (new Date()) object but the Date object will only nab you time in seconds (not milliseconds):
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
var start = performance.now();
fn();
return performance.now() - start;
}
console.log("Function took " + FPS(clicking) + " milliseconds!");

Here is some pseudo code to get what I think you're looking for - assuming you have a game loop and you're calling FPS in your game loop. As I said - more details about the specifics (such as components) of your game would be helpful.
var clicks = 0;
var fps = 0;
var elapsedTime;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
// Get start time
//call fn
// Get stop time
// var delta = stop time - start time
// elapsedTime += delta;
// fps++;
// If elapsedTime > 1 second
// then while elapsedTime > 1 second... elapsedTime -= 1 second and fps = 0;
// We use the while loop here in the event that it took more than 1 second in the call
// But you could just reset elapsedTime back to 0
}
This FPS(fn) would be called anywhere in your game in place of the original function to see how many times that function is called a second.

To answer the original question:
Okay so to clarify I dont want to add a variable to the actual function clicking I would like to be able to call something like FPS(clicking) and have the function return a value for example
Firstly you need to return a value from your clicking function, or any function you plan to pass to the FPS method.
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
return clicks;
}
Then in your FPS function, you need to return that value as well.
//function to calculate fps where fn is the name of the function
function FPS(fn){
//do stuff
return fn();
}

Related

SetInterval not completly accurate [duplicate]

I need to create a simple but accurate timer.
This is my code:
var seconds = 0;
setInterval(function() {
timer.innerHTML = seconds++;
}, 1000);
After exactly 3600 seconds, it prints about 3500 seconds.
Why is it not accurate?
How can I create an accurate timer?
Why is it not accurate?
Because you are using setTimeout() or setInterval(). They cannot be trusted, there are no accuracy guarantees for them. They are allowed to lag arbitrarily, and they do not keep a constant pace but tend to drift (as you have observed).
How can I create an accurate timer?
Use the Date object instead to get the (millisecond-)accurate, current time. Then base your logic on the current time value, instead of counting how often your callback has been executed.
For a simple timer or clock, keep track of the time difference explicitly:
var start = Date.now();
setInterval(function() {
var delta = Date.now() - start; // milliseconds elapsed since start
…
output(Math.floor(delta / 1000)); // in seconds
// alternatively just show wall clock time:
output(new Date().toUTCString());
}, 1000); // update about every second
Now, that has the problem of possibly jumping values. When the interval lags a bit and executes your callback after 990, 1993, 2996, 3999, 5002 milliseconds, you will see the second count 0, 1, 2, 3, 5 (!). So it would be advisable to update more often, like about every 100ms, to avoid such jumps.
However, sometimes you really need a steady interval executing your callbacks without drifting. This requires a bit more advanced strategy (and code), though it pays out well (and registers less timeouts). Those are known as self-adjusting timers. Here the exact delay for each of the repeated timeouts is adapted to the actually elapsed time, compared to the expected intervals:
var interval = 1000; // ms
var expected = Date.now() + interval;
setTimeout(step, interval);
function step() {
var dt = Date.now() - expected; // the drift (positive for overshooting)
if (dt > interval) {
// something really bad happened. Maybe the browser (tab) was inactive?
// possibly special handling to avoid futile "catch up" run
}
… // do what is to be done
expected += interval;
setTimeout(step, Math.max(0, interval - dt)); // take into account drift
}
I'ma just build on Bergi's answer (specifically the second part) a little bit because I really liked the way it was done, but I want the option to stop the timer once it starts (like clearInterval() almost). Sooo... I've wrapped it up into a constructor function so we can do 'objecty' things with it.
1. Constructor
Alright, so you copy/paste that...
/**
* Self-adjusting interval to account for drifting
*
* #param {function} workFunc Callback containing the work to be done
* for each interval
* #param {int} interval Interval speed (in milliseconds)
* #param {function} errorFunc (Optional) Callback to run if the drift
* exceeds interval
*/
function AdjustingInterval(workFunc, interval, errorFunc) {
var that = this;
var expected, timeout;
this.interval = interval;
this.start = function() {
expected = Date.now() + this.interval;
timeout = setTimeout(step, this.interval);
}
this.stop = function() {
clearTimeout(timeout);
}
function step() {
var drift = Date.now() - expected;
if (drift > that.interval) {
// You could have some default stuff here too...
if (errorFunc) errorFunc();
}
workFunc();
expected += that.interval;
timeout = setTimeout(step, Math.max(0, that.interval-drift));
}
}
2. Instantiate
Tell it what to do and all that...
// For testing purposes, we'll just increment
// this and send it out to the console.
var justSomeNumber = 0;
// Define the work to be done
var doWork = function() {
console.log(++justSomeNumber);
};
// Define what to do if something goes wrong
var doError = function() {
console.warn('The drift exceeded the interval.');
};
// (The third argument is optional)
var ticker = new AdjustingInterval(doWork, 1000, doError);
3. Then do... stuff
// You can start or stop your timer at will
ticker.start();
ticker.stop();
// You can also change the interval while it's in progress
ticker.interval = 99;
I mean, it works for me anyway. If there's a better way, lemme know.
Bergi's answer pinpoints exactly why the timer from the question is not accurate. Here's my take on a simple JS timer with start, stop, reset and getTime methods:
class Timer {
constructor () {
this.isRunning = false;
this.startTime = 0;
this.overallTime = 0;
}
_getTimeElapsedSinceLastStart () {
if (!this.startTime) {
return 0;
}
return Date.now() - this.startTime;
}
start () {
if (this.isRunning) {
return console.error('Timer is already running');
}
this.isRunning = true;
this.startTime = Date.now();
}
stop () {
if (!this.isRunning) {
return console.error('Timer is already stopped');
}
this.isRunning = false;
this.overallTime = this.overallTime + this._getTimeElapsedSinceLastStart();
}
reset () {
this.overallTime = 0;
if (this.isRunning) {
this.startTime = Date.now();
return;
}
this.startTime = 0;
}
getTime () {
if (!this.startTime) {
return 0;
}
if (this.isRunning) {
return this.overallTime + this._getTimeElapsedSinceLastStart();
}
return this.overallTime;
}
}
const timer = new Timer();
timer.start();
setInterval(() => {
const timeInSeconds = Math.round(timer.getTime() / 1000);
document.getElementById('time').innerText = timeInSeconds;
}, 100)
<p>Elapsed time: <span id="time">0</span>s</p>
The snippet also includes a solution for your problem. So instead of incrementing seconds variable every 1000ms interval, we just start the timer and then every 100ms* we just read elapsed time from the timer and update the view accordingly.
* - makes it more accurate than 1000ms
To make your timer more accurate, you would have to round
Most of the timers in the answers here will linger behind the expected time because they set the "expected" value to the ideal and only account for the delay that the browser introduced before that point. This is fine if you just need accurate intervals, but if you are timing relative to other events then you will (nearly) always have this delay.
To correct it, you can keep track of the drift history and use it to predict future drift. By adding a secondary adjustment with this preemptive correction, the variance in the drift centers around the target time. For example, if you're always getting a drift of 20 to 40ms, this adjustment would shift it to -10 to +10ms around the target time.
Building on Bergi's answer, I've used a rolling median for my prediction algorithm. Taking just 10 samples with this method makes a reasonable difference.
var interval = 200; // ms
var expected = Date.now() + interval;
var drift_history = [];
var drift_history_samples = 10;
var drift_correction = 0;
function calc_drift(arr){
// Calculate drift correction.
/*
In this example I've used a simple median.
You can use other methods, but it's important not to use an average.
If the user switches tabs and back, an average would put far too much
weight on the outlier.
*/
var values = arr.concat(); // copy array so it isn't mutated
values.sort(function(a,b){
return a-b;
});
if(values.length ===0) return 0;
var half = Math.floor(values.length / 2);
if (values.length % 2) return values[half];
var median = (values[half - 1] + values[half]) / 2.0;
return median;
}
setTimeout(step, interval);
function step() {
var dt = Date.now() - expected; // the drift (positive for overshooting)
if (dt > interval) {
// something really bad happened. Maybe the browser (tab) was inactive?
// possibly special handling to avoid futile "catch up" run
}
// do what is to be done
// don't update the history for exceptionally large values
if (dt <= interval) {
// sample drift amount to history after removing current correction
// (add to remove because the correction is applied by subtraction)
drift_history.push(dt + drift_correction);
// predict new drift correction
drift_correction = calc_drift(drift_history);
// cap and refresh samples
if (drift_history.length >= drift_history_samples) {
drift_history.shift();
}
}
expected += interval;
// take into account drift with prediction
setTimeout(step, Math.max(0, interval - dt - drift_correction));
}
I agree with Bergi on using Date, but his solution was a bit of overkill for my use. I simply wanted my animated clock (digital and analog SVGs) to update on the second and not overrun or under run creating obvious jumps in the clock updates. Here is the snippet of code I put in my clock update functions:
var milliseconds = now.getMilliseconds();
var newTimeout = 1000 - milliseconds;
this.timeoutVariable = setTimeout((function(thisObj) { return function() { thisObj.update(); } })(this), newTimeout);
It simply calculates the delta time to the next even second, and sets the timeout to that delta. This syncs all of my clock objects to the second. Hope this is helpful.
Here's a solution that pauses when the window is hidden, and can be cancelled with an abort controller.
function animationInterval(ms, signal, callback) {
const start = document.timeline.currentTime;
function frame(time) {
if (signal.aborted) return;
callback(time);
scheduleFrame(time);
}
function scheduleFrame(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / ms) * ms;
const targetNext = start + roundedElapsed + ms;
const delay = targetNext - performance.now();
setTimeout(() => requestAnimationFrame(frame), delay);
}
scheduleFrame(start);
}
Usage:
const controller = new AbortController();
// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
console.log('tick!', time);
});
// And stop it sometime later:
controller.abort();
Modern, Fully Programmable Timer
This timer takes a frequency in Hertz, and a callback that can take up to four arguments, the current frame index, the current time, the time that the current frame would have ideally occurred at, and a reference to the timer instance (so the caller and callback can both access its methods).
Note: All times are based on performance.now, and are relative to the moment that the page loaded.
Timer instances have three API methods:
stop: Takes no args. Kills the timer immediately (and permanently).
Returns the frame index for the next frame (the cancelled frame).
adapt: Takes a frequency in Hertz and adapts the timer to it, beginning
from the next frame. Returns the implied interval in milliseconds.
redefine: Takes a new callback function. Swaps it with the current
callback. Effects the next frame. Returns undefined.
Note: The tick method passes this around explicitly (as self) to work around the problem of this referencing window when the tick method is invoked via setTimeout.
class ProgrammableTimer {
constructor(hertz, callback) {
this.target = performance.now(); // target time for the next frame
this.interval = 1 / hertz * 1000; // the milliseconds between ticks
this.callback = callback;
this.stopped = false;
this.frame = 0;
this.tick(this);
}
tick(self) {
if (self.stopped) return;
const currentTime = performance.now();
const currentTarget = self.target;
const currentInterval = (self.target += self.interval) - currentTime;
setTimeout(self.tick, currentInterval, self);
self.callback(self.frame++, currentTime, currentTarget, self);
}
stop() { this.stopped = true; return this.frame }
adapt(hertz) { return this.interval = 1 / hertz * 1000 }
redefine(replacement) { this.callback = replacement }
}
Doesn't get much more accurate than this.
var seconds = new Date().getTime(), last = seconds,
intrvl = setInterval(function() {
var now = new Date().getTime();
if(now - last > 5){
if(confirm("Delay registered, terminate?")){
clearInterval(intrvl);
return;
}
}
last = now;
timer.innerHTML = now - seconds;
}, 333);
As to why it is not accurate, I would guess that the machine is busy doing other things, slowing down a little on each iteration adds up, as you see.
This is an old question but figured I'd share some code I use sometimes:
function Timer(func, delay, repeat, runAtStart)
{
this.func = func;
this.delay = delay;
this.repeat = repeat || 0;
this.runAtStart = runAtStart;
this.count = 0;
this.startTime = performance.now();
if (this.runAtStart)
this.tick();
else
{
var _this = this;
this.timeout = window.setTimeout( function(){ _this.tick(); }, this.delay);
}
}
Timer.prototype.tick = function()
{
this.func();
this.count++;
if (this.repeat === -1 || (this.repeat > 0 && this.count < this.repeat) )
{
var adjustedDelay = Math.max( 1, this.startTime + ( (this.count+(this.runAtStart ? 2 : 1)) * this.delay ) - performance.now() );
var _this = this;
this.timeout = window.setTimeout( function(){ _this.tick(); }, adjustedDelay);
}
}
Timer.prototype.stop = function()
{
window.clearTimeout(this.timeout);
}
Example:
time = 0;
this.gameTimer = new Timer( function() { time++; }, 1000, -1);
Self-corrects the setTimeout, can run it X number of times (-1 for infinite), can start running instantaneously, and has a counter if you ever need to see how many times the func() has been run. Comes in handy.
Edit: Note, this doesn't do any input checking (like if delay and repeat are the correct type. And you'd probably want to add some kind of get/set function if you wanted to get the count or change the repeat value.
One of my simplest implementations is down below. It can even survive page reloads. :-
Code pen: https://codepen.io/shivabhusal/pen/abvmgaV
$(function() {
var TTimer = {
startedTime: new Date(),
restoredFromSession: false,
started: false,
minutes: 0,
seconds: 0,
tick: function tick() {
// Since setInterval is not reliable in inactive windows/tabs we are using date diff.
var diffInSeconds = Math.floor((new Date() - this.startedTime) / 1000);
this.minutes = Math.floor(diffInSeconds / 60);
this.seconds = diffInSeconds - this.minutes * 60;
this.render();
this.updateSession();
},
utilities: {
pad: function pad(number) {
return number < 10 ? '0' + number : number;
}
},
container: function container() {
return $(document);
},
render: function render() {
this.container().find('#timer-minutes').text(this.utilities.pad(this.minutes));
this.container().find('#timer-seconds').text(this.utilities.pad(this.seconds));
},
updateSession: function updateSession() {
sessionStorage.setItem('timerStartedTime', this.startedTime);
},
clearSession: function clearSession() {
sessionStorage.removeItem('timerStartedTime');
},
restoreFromSession: function restoreFromSession() {
// Using sessionsStorage to make the timer persistent
if (typeof Storage == "undefined") {
console.log('No sessionStorage Support');
return;
}
if (sessionStorage.getItem('timerStartedTime') !== null) {
this.restoredFromSession = true;
this.startedTime = new Date(sessionStorage.getItem('timerStartedTime'));
}
},
start: function start() {
this.restoreFromSession();
this.stop();
this.started = true;
this.tick();
this.timerId = setInterval(this.tick.bind(this), 1000);
},
stop: function stop() {
this.started = false;
clearInterval(this.timerId);
this.render();
}
};
TTimer.start();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<h1>
<span id="timer-minutes">00</span> :
<span id="timer-seconds">00</span>
</h1>
Inspired by Bergi's answer I created the following complete non drifting timer. What I wanted was a way to set a timer, stop it, and do this simply.
var perfectTimer = { // Set of functions designed to create nearly perfect timers that do not drift
timers: {}, // An object of timers by ID
nextID: 0, // Next available timer reference ID
set: (callback, interval) => { // Set a timer
var expected = Date.now() + interval; // Expected currect time when timeout fires
var ID = perfectTimer.nextID++; // Create reference to timer
function step() { // Adjusts the timeout to account for any drift since last timeout
callback(); // Call the callback
var dt = Date.now() - expected; // The drift (ms) (positive for overshooting) comparing the expected time to the current time
expected += interval; // Set the next expected currect time when timeout fires
perfectTimer.timers[ID] = setTimeout(step, Math.max(0, interval - dt)); // Take into account drift
}
perfectTimer.timers[ID] = setTimeout(step, interval); // Return reference to timer
return ID;
},
clear: (ID) => { // Clear & delete a timer by ID reference
if (perfectTimer.timers[ID] != undefined) { // Preventing errors when trying to clear a timer that no longer exists
console.log('clear timer:', ID);
console.log('timers before:', perfectTimer.timers);
clearTimeout(perfectTimer.timers[ID]); // Clear timer
delete perfectTimer.timers[ID]; // Delete timer reference
console.log('timers after:', perfectTimer.timers);
}
}
}
// Below are some tests
var timerOne = perfectTimer.set(() => {
console.log(new Date().toString(), Date.now(), 'timerOne', timerOne);
}, 1000);
console.log(timerOne);
setTimeout(() => {
perfectTimer.clear(timerOne);
}, 5000)
var timerTwo = perfectTimer.set(() => {
console.log(new Date().toString(), Date.now(), 'timerTwo', timerTwo);
}, 1000);
console.log(timerTwo);
setTimeout(() => {
perfectTimer.clear(timerTwo);
}, 8000)
driftless is a drop-in replacement for setInterval that mitigates drift. Makes life easy, import the npm package, then use it like setInterval / setTimeout:
setDriftlessInterval(() => {
this.counter--;
}, 1000);
setDriftlessInterval(() => {
this.refreshBounds();
}, 20000);
you can use a function called setTimeout that we can use to set the countdown.
Firstly, create a javascript snippet and add it to your page as follows;
var remainingTime = 30;
var elem = document.getElementById('countdown_div');
var timer = setInterval(countdown, 1000); //set the countdown to every second
function countdown() {
if (remainingTime == -1) {
clearTimeout(timer);
doSomething();
} else {
elem.innerHTML = remainingTime + ' left';
remainingTime--; //we subtract the second each iteration
}
}
Source + more details -> https://www.growthsnippets.com/30-second-countdown-timer-javascript/
Many of these answers here are great, but they typically their code examples are pages and pages of code (the good ones even have instructions on the best way to copy/paste it all). I just wanted to understand this problem with a very simple example.
Working Demo
var lastpause = 0;
var totaltime = 0;
function goFunction(e) {
if(this.innerText == 'Off - Timer Not Going') {
this.innerText = 'On - Timer Going';
} else {
totaltime += Date.now() - lastpause;
this.innerText = 'Off - Timer Not Going';
}
lastpause = Date.now();
document.getElementById('count').innerText = totaltime + ' milliseconds.';
}
document.getElementById('button').addEventListener('click', goFunction);
<button id="button">Off - Timer Not Going</button> <br>
Seconds: <span id="count">0 milliseconds.</span>
Explanation of Demo
totaltime — This is the total time calculated.
lastpause — This is the only real temporary variable we have. Whenever someone hits pause, we set lastpause to Date.now(). When someone unpauses, and re-pauses again, we calculate the time diff of Date.now() subtracted from the last pause.
We only need those two variables: Our total and the last time we stopped the timer. The other answers seem to use this approach, but I wanted a compact explanation.
I gave up building my own and ended up using this neat library.

Changing a specific thing in a varible

I have a equation like this stored in a varible
(50 * 1.07^1) its very simple. I want to know how I can change the power each time a function runs like so: 50*1.07^2, 50*1.07^3 and so forth. Any help?
Here is my code:
var mathForCost = 50 * 1.07 ^ 1;
function gainCoinsPS() {
if (coins >= costPS) {
coinsPS += 10;
coins -= costPS;
// Here is where I am changing the cost each time the function runs,
// so I need to make the power(^1) add 1 each time
costPS = document.getElementById("changePrice1").innerHTML = "Cost: " + costPS;
} else {;
alert("You dont have enough coins!");
};
}
Save the power to a variable, and you can update it when needed. It is preferred that you put the equation into a function and pass power to it, and return the solution.
var power = 1,
eq = function(p){
return 50*1.07^+p; // returns solution
};
for(var i=0; i<10; i++){
power = i;
console.log( eq(power) ); // solution
}
You can store your power in a variable and increment it each time your function is called.
var power = 1;
function calculate() {
console.log(50 * Math.pow(1.07, power));
power++;
}
calculate();
calculate();
calculate();
In Javascript you can't really store an equation in a variable, except maybe as a string (but that is fraught with issues of its own). Your function will be evaluated the moment you execute, and the value of the output will instead be stored in the variable.
To do what you want to do, you would be better having a function that runs the equation, and increments the power each time-- this works if the power is in a higher scope (or it can be accomplished with a closure)
var power = 1;
function getCost()
var cost = Math.pow(50*1.07, power);
power++;
return cost;
}
Each time this function runs, it returns the calculated cost and also increments the value of power, so it will be one higher the next time it runs.
Alternately, if you wanted to go the closure route, you could do something like this:
var getCost = (function () {
var power = 1;
return function () {
var cost = Math.pow(50*1.07, power);
console.log(power);
power++;
return cost;
}
})();
You can store a state to the function that runs the equation. This helps you avoid adding more state outside of the function. Let the function keep track of how many times it has been called.
function calc() {
if (!this.i) {
this.i = 1;
}
return (50 * Math.pow(1.07, this.i++));
}
console.log(calc());
console.log(calc());
console.log(calc());
There is Math.pow function is javascript for this.
You can use something like this
var pow = 1;
for(var power=1; power<limit; power++){ // run the loop upto a limit
console.log(Math.pow(50*1.07, power);
}
To increment power of 1.07 by 1, just multiply value by 1.07 every time (pow function is not needed at all)
var mathForCost = 50 * 1.07;
...
mathForCost = mathForCost * 1.07;
You could use a function for it.
getCost = function (n) { return 50 * Math.pow(1.07, n); };
Or with ES6's arrow function
getCost = n => 50 * Math.pow(1.07, n);
Call it with
value = getCost(1);

How to fix setInterval issue when the clamped minimum is slower than the one second for inactive tab? [duplicate]

I need to create a simple but accurate timer.
This is my code:
var seconds = 0;
setInterval(function() {
timer.innerHTML = seconds++;
}, 1000);
After exactly 3600 seconds, it prints about 3500 seconds.
Why is it not accurate?
How can I create an accurate timer?
Why is it not accurate?
Because you are using setTimeout() or setInterval(). They cannot be trusted, there are no accuracy guarantees for them. They are allowed to lag arbitrarily, and they do not keep a constant pace but tend to drift (as you have observed).
How can I create an accurate timer?
Use the Date object instead to get the (millisecond-)accurate, current time. Then base your logic on the current time value, instead of counting how often your callback has been executed.
For a simple timer or clock, keep track of the time difference explicitly:
var start = Date.now();
setInterval(function() {
var delta = Date.now() - start; // milliseconds elapsed since start
…
output(Math.floor(delta / 1000)); // in seconds
// alternatively just show wall clock time:
output(new Date().toUTCString());
}, 1000); // update about every second
Now, that has the problem of possibly jumping values. When the interval lags a bit and executes your callback after 990, 1993, 2996, 3999, 5002 milliseconds, you will see the second count 0, 1, 2, 3, 5 (!). So it would be advisable to update more often, like about every 100ms, to avoid such jumps.
However, sometimes you really need a steady interval executing your callbacks without drifting. This requires a bit more advanced strategy (and code), though it pays out well (and registers less timeouts). Those are known as self-adjusting timers. Here the exact delay for each of the repeated timeouts is adapted to the actually elapsed time, compared to the expected intervals:
var interval = 1000; // ms
var expected = Date.now() + interval;
setTimeout(step, interval);
function step() {
var dt = Date.now() - expected; // the drift (positive for overshooting)
if (dt > interval) {
// something really bad happened. Maybe the browser (tab) was inactive?
// possibly special handling to avoid futile "catch up" run
}
… // do what is to be done
expected += interval;
setTimeout(step, Math.max(0, interval - dt)); // take into account drift
}
I'ma just build on Bergi's answer (specifically the second part) a little bit because I really liked the way it was done, but I want the option to stop the timer once it starts (like clearInterval() almost). Sooo... I've wrapped it up into a constructor function so we can do 'objecty' things with it.
1. Constructor
Alright, so you copy/paste that...
/**
* Self-adjusting interval to account for drifting
*
* #param {function} workFunc Callback containing the work to be done
* for each interval
* #param {int} interval Interval speed (in milliseconds)
* #param {function} errorFunc (Optional) Callback to run if the drift
* exceeds interval
*/
function AdjustingInterval(workFunc, interval, errorFunc) {
var that = this;
var expected, timeout;
this.interval = interval;
this.start = function() {
expected = Date.now() + this.interval;
timeout = setTimeout(step, this.interval);
}
this.stop = function() {
clearTimeout(timeout);
}
function step() {
var drift = Date.now() - expected;
if (drift > that.interval) {
// You could have some default stuff here too...
if (errorFunc) errorFunc();
}
workFunc();
expected += that.interval;
timeout = setTimeout(step, Math.max(0, that.interval-drift));
}
}
2. Instantiate
Tell it what to do and all that...
// For testing purposes, we'll just increment
// this and send it out to the console.
var justSomeNumber = 0;
// Define the work to be done
var doWork = function() {
console.log(++justSomeNumber);
};
// Define what to do if something goes wrong
var doError = function() {
console.warn('The drift exceeded the interval.');
};
// (The third argument is optional)
var ticker = new AdjustingInterval(doWork, 1000, doError);
3. Then do... stuff
// You can start or stop your timer at will
ticker.start();
ticker.stop();
// You can also change the interval while it's in progress
ticker.interval = 99;
I mean, it works for me anyway. If there's a better way, lemme know.
Bergi's answer pinpoints exactly why the timer from the question is not accurate. Here's my take on a simple JS timer with start, stop, reset and getTime methods:
class Timer {
constructor () {
this.isRunning = false;
this.startTime = 0;
this.overallTime = 0;
}
_getTimeElapsedSinceLastStart () {
if (!this.startTime) {
return 0;
}
return Date.now() - this.startTime;
}
start () {
if (this.isRunning) {
return console.error('Timer is already running');
}
this.isRunning = true;
this.startTime = Date.now();
}
stop () {
if (!this.isRunning) {
return console.error('Timer is already stopped');
}
this.isRunning = false;
this.overallTime = this.overallTime + this._getTimeElapsedSinceLastStart();
}
reset () {
this.overallTime = 0;
if (this.isRunning) {
this.startTime = Date.now();
return;
}
this.startTime = 0;
}
getTime () {
if (!this.startTime) {
return 0;
}
if (this.isRunning) {
return this.overallTime + this._getTimeElapsedSinceLastStart();
}
return this.overallTime;
}
}
const timer = new Timer();
timer.start();
setInterval(() => {
const timeInSeconds = Math.round(timer.getTime() / 1000);
document.getElementById('time').innerText = timeInSeconds;
}, 100)
<p>Elapsed time: <span id="time">0</span>s</p>
The snippet also includes a solution for your problem. So instead of incrementing seconds variable every 1000ms interval, we just start the timer and then every 100ms* we just read elapsed time from the timer and update the view accordingly.
* - makes it more accurate than 1000ms
To make your timer more accurate, you would have to round
Most of the timers in the answers here will linger behind the expected time because they set the "expected" value to the ideal and only account for the delay that the browser introduced before that point. This is fine if you just need accurate intervals, but if you are timing relative to other events then you will (nearly) always have this delay.
To correct it, you can keep track of the drift history and use it to predict future drift. By adding a secondary adjustment with this preemptive correction, the variance in the drift centers around the target time. For example, if you're always getting a drift of 20 to 40ms, this adjustment would shift it to -10 to +10ms around the target time.
Building on Bergi's answer, I've used a rolling median for my prediction algorithm. Taking just 10 samples with this method makes a reasonable difference.
var interval = 200; // ms
var expected = Date.now() + interval;
var drift_history = [];
var drift_history_samples = 10;
var drift_correction = 0;
function calc_drift(arr){
// Calculate drift correction.
/*
In this example I've used a simple median.
You can use other methods, but it's important not to use an average.
If the user switches tabs and back, an average would put far too much
weight on the outlier.
*/
var values = arr.concat(); // copy array so it isn't mutated
values.sort(function(a,b){
return a-b;
});
if(values.length ===0) return 0;
var half = Math.floor(values.length / 2);
if (values.length % 2) return values[half];
var median = (values[half - 1] + values[half]) / 2.0;
return median;
}
setTimeout(step, interval);
function step() {
var dt = Date.now() - expected; // the drift (positive for overshooting)
if (dt > interval) {
// something really bad happened. Maybe the browser (tab) was inactive?
// possibly special handling to avoid futile "catch up" run
}
// do what is to be done
// don't update the history for exceptionally large values
if (dt <= interval) {
// sample drift amount to history after removing current correction
// (add to remove because the correction is applied by subtraction)
drift_history.push(dt + drift_correction);
// predict new drift correction
drift_correction = calc_drift(drift_history);
// cap and refresh samples
if (drift_history.length >= drift_history_samples) {
drift_history.shift();
}
}
expected += interval;
// take into account drift with prediction
setTimeout(step, Math.max(0, interval - dt - drift_correction));
}
I agree with Bergi on using Date, but his solution was a bit of overkill for my use. I simply wanted my animated clock (digital and analog SVGs) to update on the second and not overrun or under run creating obvious jumps in the clock updates. Here is the snippet of code I put in my clock update functions:
var milliseconds = now.getMilliseconds();
var newTimeout = 1000 - milliseconds;
this.timeoutVariable = setTimeout((function(thisObj) { return function() { thisObj.update(); } })(this), newTimeout);
It simply calculates the delta time to the next even second, and sets the timeout to that delta. This syncs all of my clock objects to the second. Hope this is helpful.
Here's a solution that pauses when the window is hidden, and can be cancelled with an abort controller.
function animationInterval(ms, signal, callback) {
const start = document.timeline.currentTime;
function frame(time) {
if (signal.aborted) return;
callback(time);
scheduleFrame(time);
}
function scheduleFrame(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / ms) * ms;
const targetNext = start + roundedElapsed + ms;
const delay = targetNext - performance.now();
setTimeout(() => requestAnimationFrame(frame), delay);
}
scheduleFrame(start);
}
Usage:
const controller = new AbortController();
// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
console.log('tick!', time);
});
// And stop it sometime later:
controller.abort();
Modern, Fully Programmable Timer
This timer takes a frequency in Hertz, and a callback that can take up to four arguments, the current frame index, the current time, the time that the current frame would have ideally occurred at, and a reference to the timer instance (so the caller and callback can both access its methods).
Note: All times are based on performance.now, and are relative to the moment that the page loaded.
Timer instances have three API methods:
stop: Takes no args. Kills the timer immediately (and permanently).
Returns the frame index for the next frame (the cancelled frame).
adapt: Takes a frequency in Hertz and adapts the timer to it, beginning
from the next frame. Returns the implied interval in milliseconds.
redefine: Takes a new callback function. Swaps it with the current
callback. Effects the next frame. Returns undefined.
Note: The tick method passes this around explicitly (as self) to work around the problem of this referencing window when the tick method is invoked via setTimeout.
class ProgrammableTimer {
constructor(hertz, callback) {
this.target = performance.now(); // target time for the next frame
this.interval = 1 / hertz * 1000; // the milliseconds between ticks
this.callback = callback;
this.stopped = false;
this.frame = 0;
this.tick(this);
}
tick(self) {
if (self.stopped) return;
const currentTime = performance.now();
const currentTarget = self.target;
const currentInterval = (self.target += self.interval) - currentTime;
setTimeout(self.tick, currentInterval, self);
self.callback(self.frame++, currentTime, currentTarget, self);
}
stop() { this.stopped = true; return this.frame }
adapt(hertz) { return this.interval = 1 / hertz * 1000 }
redefine(replacement) { this.callback = replacement }
}
Doesn't get much more accurate than this.
var seconds = new Date().getTime(), last = seconds,
intrvl = setInterval(function() {
var now = new Date().getTime();
if(now - last > 5){
if(confirm("Delay registered, terminate?")){
clearInterval(intrvl);
return;
}
}
last = now;
timer.innerHTML = now - seconds;
}, 333);
As to why it is not accurate, I would guess that the machine is busy doing other things, slowing down a little on each iteration adds up, as you see.
This is an old question but figured I'd share some code I use sometimes:
function Timer(func, delay, repeat, runAtStart)
{
this.func = func;
this.delay = delay;
this.repeat = repeat || 0;
this.runAtStart = runAtStart;
this.count = 0;
this.startTime = performance.now();
if (this.runAtStart)
this.tick();
else
{
var _this = this;
this.timeout = window.setTimeout( function(){ _this.tick(); }, this.delay);
}
}
Timer.prototype.tick = function()
{
this.func();
this.count++;
if (this.repeat === -1 || (this.repeat > 0 && this.count < this.repeat) )
{
var adjustedDelay = Math.max( 1, this.startTime + ( (this.count+(this.runAtStart ? 2 : 1)) * this.delay ) - performance.now() );
var _this = this;
this.timeout = window.setTimeout( function(){ _this.tick(); }, adjustedDelay);
}
}
Timer.prototype.stop = function()
{
window.clearTimeout(this.timeout);
}
Example:
time = 0;
this.gameTimer = new Timer( function() { time++; }, 1000, -1);
Self-corrects the setTimeout, can run it X number of times (-1 for infinite), can start running instantaneously, and has a counter if you ever need to see how many times the func() has been run. Comes in handy.
Edit: Note, this doesn't do any input checking (like if delay and repeat are the correct type. And you'd probably want to add some kind of get/set function if you wanted to get the count or change the repeat value.
One of my simplest implementations is down below. It can even survive page reloads. :-
Code pen: https://codepen.io/shivabhusal/pen/abvmgaV
$(function() {
var TTimer = {
startedTime: new Date(),
restoredFromSession: false,
started: false,
minutes: 0,
seconds: 0,
tick: function tick() {
// Since setInterval is not reliable in inactive windows/tabs we are using date diff.
var diffInSeconds = Math.floor((new Date() - this.startedTime) / 1000);
this.minutes = Math.floor(diffInSeconds / 60);
this.seconds = diffInSeconds - this.minutes * 60;
this.render();
this.updateSession();
},
utilities: {
pad: function pad(number) {
return number < 10 ? '0' + number : number;
}
},
container: function container() {
return $(document);
},
render: function render() {
this.container().find('#timer-minutes').text(this.utilities.pad(this.minutes));
this.container().find('#timer-seconds').text(this.utilities.pad(this.seconds));
},
updateSession: function updateSession() {
sessionStorage.setItem('timerStartedTime', this.startedTime);
},
clearSession: function clearSession() {
sessionStorage.removeItem('timerStartedTime');
},
restoreFromSession: function restoreFromSession() {
// Using sessionsStorage to make the timer persistent
if (typeof Storage == "undefined") {
console.log('No sessionStorage Support');
return;
}
if (sessionStorage.getItem('timerStartedTime') !== null) {
this.restoredFromSession = true;
this.startedTime = new Date(sessionStorage.getItem('timerStartedTime'));
}
},
start: function start() {
this.restoreFromSession();
this.stop();
this.started = true;
this.tick();
this.timerId = setInterval(this.tick.bind(this), 1000);
},
stop: function stop() {
this.started = false;
clearInterval(this.timerId);
this.render();
}
};
TTimer.start();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<h1>
<span id="timer-minutes">00</span> :
<span id="timer-seconds">00</span>
</h1>
Inspired by Bergi's answer I created the following complete non drifting timer. What I wanted was a way to set a timer, stop it, and do this simply.
var perfectTimer = { // Set of functions designed to create nearly perfect timers that do not drift
timers: {}, // An object of timers by ID
nextID: 0, // Next available timer reference ID
set: (callback, interval) => { // Set a timer
var expected = Date.now() + interval; // Expected currect time when timeout fires
var ID = perfectTimer.nextID++; // Create reference to timer
function step() { // Adjusts the timeout to account for any drift since last timeout
callback(); // Call the callback
var dt = Date.now() - expected; // The drift (ms) (positive for overshooting) comparing the expected time to the current time
expected += interval; // Set the next expected currect time when timeout fires
perfectTimer.timers[ID] = setTimeout(step, Math.max(0, interval - dt)); // Take into account drift
}
perfectTimer.timers[ID] = setTimeout(step, interval); // Return reference to timer
return ID;
},
clear: (ID) => { // Clear & delete a timer by ID reference
if (perfectTimer.timers[ID] != undefined) { // Preventing errors when trying to clear a timer that no longer exists
console.log('clear timer:', ID);
console.log('timers before:', perfectTimer.timers);
clearTimeout(perfectTimer.timers[ID]); // Clear timer
delete perfectTimer.timers[ID]; // Delete timer reference
console.log('timers after:', perfectTimer.timers);
}
}
}
// Below are some tests
var timerOne = perfectTimer.set(() => {
console.log(new Date().toString(), Date.now(), 'timerOne', timerOne);
}, 1000);
console.log(timerOne);
setTimeout(() => {
perfectTimer.clear(timerOne);
}, 5000)
var timerTwo = perfectTimer.set(() => {
console.log(new Date().toString(), Date.now(), 'timerTwo', timerTwo);
}, 1000);
console.log(timerTwo);
setTimeout(() => {
perfectTimer.clear(timerTwo);
}, 8000)
driftless is a drop-in replacement for setInterval that mitigates drift. Makes life easy, import the npm package, then use it like setInterval / setTimeout:
setDriftlessInterval(() => {
this.counter--;
}, 1000);
setDriftlessInterval(() => {
this.refreshBounds();
}, 20000);
you can use a function called setTimeout that we can use to set the countdown.
Firstly, create a javascript snippet and add it to your page as follows;
var remainingTime = 30;
var elem = document.getElementById('countdown_div');
var timer = setInterval(countdown, 1000); //set the countdown to every second
function countdown() {
if (remainingTime == -1) {
clearTimeout(timer);
doSomething();
} else {
elem.innerHTML = remainingTime + ' left';
remainingTime--; //we subtract the second each iteration
}
}
Source + more details -> https://www.growthsnippets.com/30-second-countdown-timer-javascript/
Many of these answers here are great, but they typically their code examples are pages and pages of code (the good ones even have instructions on the best way to copy/paste it all). I just wanted to understand this problem with a very simple example.
Working Demo
var lastpause = 0;
var totaltime = 0;
function goFunction(e) {
if(this.innerText == 'Off - Timer Not Going') {
this.innerText = 'On - Timer Going';
} else {
totaltime += Date.now() - lastpause;
this.innerText = 'Off - Timer Not Going';
}
lastpause = Date.now();
document.getElementById('count').innerText = totaltime + ' milliseconds.';
}
document.getElementById('button').addEventListener('click', goFunction);
<button id="button">Off - Timer Not Going</button> <br>
Seconds: <span id="count">0 milliseconds.</span>
Explanation of Demo
totaltime — This is the total time calculated.
lastpause — This is the only real temporary variable we have. Whenever someone hits pause, we set lastpause to Date.now(). When someone unpauses, and re-pauses again, we calculate the time diff of Date.now() subtracted from the last pause.
We only need those two variables: Our total and the last time we stopped the timer. The other answers seem to use this approach, but I wanted a compact explanation.
I gave up building my own and ended up using this neat library.

setTimeout executes faster than simultaneous setInterval

So, I have a setInterval and a setTimeout running simultaneously in this click-o-meter thing I'm doing: the user enters an specified number of seconds he/she wants the game to run for, and then it counts how many clicks you have done, what was the average time between each click, and the average amount of clicks per second you've made during the specified period of time.
<html>
<head></head>
<body>
<input type='text' id='timerInput'></input>
<button id='btn'>Click</button>
<script>
var before;
var now;
var clicks = 0;
var cts = 0; //Stands for 'Clicks This Second'
var intervals = new Array();
var cps = new Array();
var cpsCounter;
var timer;
var canContinue = true;
var timerInput = document.getElementById('timerInput');
var timerTime;
var wasBad = false;
document.getElementById('btn').onclick = function() {
if(canContinue) {
if(clicks <= 0) {
if(timerInput.value.replace(/\D/, '') === timerInput.value) {
wasBad = false;
timerTime = parseInt(timerInput.value.replace(/\D/, '')) * 1000;
before = new Date();
cpsCounter = window.setInterval(ctsFunction, 1000);
timer = window.setTimeout(finish, timerTime);
}else{
alert('Only numbers please!');
wasBad = true;
}
}else{
now = new Date();
console.log(now - before);
intervals.push(now - before);
before = new Date();
}
if(!wasBad){
clicks++;
cts++;
}
}else{console.log('Game ended');}
};
function ctsFunction() {
console.log('Clicks this second: ' + cts);
cps.push(cts);
cts = 0;
}
function finish() {
console.log('Clicks: ' + clicks);
console.log('Average Speed (ms): ' + Math.floor(intervals.reduce(function(a, b){return a + b;}) / (clicks - 1)));
console.log('Average Speed (clicks per second): ' + (cps.reduce(function(a, b){return a + b;}) / cps.length));
intervals = new Array();
console.log('cps.length: ' + cps.length);
cps = new Array();
clicks = 0;
cts = 0;
window.clearInterval(cpsCounter);
canContinue = false;
}
</script>
</body>
</html>
So, the problem is that when the gmae finishes, that is, when timer reaches the end, ctsFunction() is supposed to run once more at the last second, so it can register data from it; but finish() is executed faster, or prior to ctsFunction(), thus clearing the cpsCounter interval and not allowing it to do anything on the last second. I've tried adding some extra milliseconds to timer, but if you choose to run the game for enough seconds, the same problem will eventually happen (e.g. if you add 1ms, the problem will be solved for up to 2 seconds, but not for more).
I have a setInterval and a setTimeout running simultaneously
It will never happens because javascript is a single thread language. There is no matter what is in your code, javascript can't execute two commands simultaneously.
And one more:
timer delay is not guaranteed. JavaScript in a browser executes on a
single thread asynchronous events (such as mouse clicks and timers)
are only run when there’s been an opening in the execution.
Read this article to understand how javascript timers work.

Increase value / Script

I need some help with increases value / 1 per second.
I have
<script>
function incrementValue()
{
var value = parseInt(document.getElementById('number').value, 10);
value = isNaN(value) ? 0 : value;
value++;
document.getElementById('number').value = value;
return false;
}
</script>
Now I need a function that will increase the value of 'number' and start adding +1 every second until I click on another image.
The only thing I am capable of making is:
function incrementvale()
var incrementTime = 1;
var incrementBy = 1;
private var counter = 0;
function Start () {
InvokeRepeating("Increment", incrementTime, incrementTime);
}
function Increment () {
counter += incrementBy;
}
Which is horribly wrong!
Question!
When I click on image.
Function starts.
The function increases the value of 'number' by 1 every second.
Thanks
If you really want to increase a value exactly after 1 second I would suggest you save the start time (Unix time stamp e.g. seconds since 1/1/1970). And if you want to know the value of your increasing variable just subtract from start value.
Because you mentioned PHP and showed us JS Code I keep it in Pseudo-Code
function getUnixTimeStampNow() {
return Math.round((new Date()).getTime() / 1000);
}
var start = getUnixTimeStampNow();
// at any time do
var current_value = getUnixTimeStampNow() - start;

Categories

Resources