I'm trying to figure out the way I can set up a rolling window for a variable.
The variable will record a number increasing # amount of times from the minute before.
My basic interval timer
var kp123 = Number('1');
var myInt = setInterval(function () {
kp123 = 1;
}, 60000);
Whenever a specific command is sent, kp123 gets increased by 1:
kp123++;
This variable may increase 20 times every second, or only 2-3 times every second.
Right now how the system is set up, it records the variable # every minute, however, the data gets reset when the interval timer reaches one minute.
var kp123 = Number('1');
var kp123History = []; // The history of kp123 is stored in this variable each minute
var myInt = setInterval(function () {
kp123History.push(kp123);
kp123 = 1;
console.log(kp123History); // You can see the results in the console each minute like this
}, 60000);
or if you only want the previous value, and not the full history, try this
var kp123 = Number('1');
var prevKp123 = null; // The previous value of kp123 is stored in this variable each minute
var myInt = setInterval(function () {
prevKp123 = kp123;
kp123 = 1;
}, 60000);
It sounds like (per the comments) you want a rolling average (wiki). You don't really show enough of your code for me to give a specific answer to you, but in general, you can't deduce a rolling average from just averages, you'll need to know actual values and their timestamps. I.e. you can't summarize your data and throw away the timestmaps.
class RollingAverage {
constructor(windowMs = 1000) {
this.windowMs_ = windowMs;
this.values_ = [];
}
addValue(value = 1) {
let time = Date.now();
this.values_.push({value, time});
}
get average() {
let now = Date.now();
this.values_ = this.values_.filter(({time}) => now - time <= this.windowMs_ * 1000);
return this.values_
.map(({value}) => value)
.reduce((a, b) => a + b)
/ this.values_.length;
}
}
let test = async () => {
let sleep = ms => new Promise(r => setTimeout(r, ms));
let avg = new RollingAverage();
avg.addValue(1);
console.log(avg.average); // 1
await sleep(600);
console.log(avg.average); // 1
avg.addValue(3);
console.log(avg.average); // 2
await sleep(600);
console.log(avg.average); // 3
avg.addValue(5);
console.log(avg.average); // 4
}
test();
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
yes I'm new, and don't worry about making me sound dumb. :) This is a web app written in Google Script and it's taking info from Google Sheets.
I adopted a code from a comment here: Javascript timer to use multiple times in a page . Current code is below. I'm running several different timers thru this whole thing and they all go down by 1 second every second - great. My question is because the value on the google sheet that the first code grabs, is changing every few minutes. I WANT it to be counting down from the most recent data, and make the "old" counter STOP. From reading several related threads I think I need another clearInterval somewhere but I can't figure out where to put it.
Google Apps Script that grabs Google sheets values and puts them into a variable. the "getdata" values are dates in the future, and then "data" is how many seconds until that date:
function getfivecoins(){
var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Live");
var getdata = livesheet.getRange("D42").getValue().getTime();
var now = new Date().getTime();
var data = (getdata-now)/1000; // how many seconds in the future this date is
return data;
}
function gettencoins(){
var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Live");
var getdata = livesheet.getRange("D43").getValue().getTime();
var now = new Date().getTime();
var data = (getdata-now)/1000; // how many seconds in the future this date is
return data;
}
then in html page:
<span id="timer1"></span>
<br>
<span id="timer2"></span>
<br>
lower down inside of script tags:
// every 10 seconds, gets new values from google sheets. Countdown should continue as normal (with some milliseconds difference, whatever) if values are the same, but trying to get countdown to start again if the value has changed. If its easier, clearing all the countdowns and then starting them again should have the same result.
document.addEventListener('DOMContentLoaded', function(){
setInterval(function(){
google.script.run.withSuccessHandler(generatefivecoins).getfivecoins();
google.script.run.withSuccessHandler(generatetencoins).gettencoins();
}, 10000);
});
const clearStack = []; //Added
function generatefivecoins(result) {
clearStack.forEach(clearInterval); //Added
var timer = document.getElementById('timer1');
var t = new timerObject();
clearStack.push(t.startTimer(result, timer)); //Modified
}
function generatetencoins(result){
clearStack.forEach(clearInterval); //Added
var timer = document.getElementById("timer2");
var t = new timerObject();
clearStack.push(t.startTimer(result, timer)); //Modified
}
var timerObject = function(){
return this;
};
timerObject.prototype.startTimer = function(duration, display) {
var me = this,
timer = duration,
STYLETEST, hours, minutes, seconds, ENDSTYLETEST;
var intervalLoop = setInterval(() => { // modified this line
//fancy colours and formatting stuff goes here
display.innerHTML = STYLETEST + " " + hours + ":" + minutes + ":" + seconds + ENDSTYLETEST;
if (--timer < 0) {
clearInterval(intervalLoop);
}
}, 1000);
return intervalLoop; //Added
};
Flow:
Instead of multiple functions, We use a single function with variable arguments.
coinBucket holds the different coins that can be generated
After generateCoins is called from server side, We use Map to store timerObject instance of class . During the next call, We use the same timerObject instead of creating a new one.
code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index');
}
const getCoins = coin => {
const map = { 5: 'D42', 10: 'D43', 20: 'D48', 60: 'D49', 90: 'D50' };//fivecoin range = D42 and so on
var livesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Live');
var getdata = livesheet
.getRange(map[coin])
.getValue()
.getTime();
var now = new Date().getTime();
var data = getdata - now; // how many milliseconds in the future this date is
return data;
};
Client side:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<h1>Coins!!</h1>
<span id="timer1"></span>
<span id="timer2"></span>
<span id="timer3"></span>
<span id="timer4"></span>
<span id="timer5"></span>
</body>
<script>
const coinBucket = [5, 10, 20, 60, 90];//number of coin buckets
const GSR = (func, ...args) =>// google.script.run to promise
new Promise((resolve, reject) =>
google.script.run
.withSuccessHandler(resolve)
.withFailureHandler(reject)
[func](...args)
);
const clearStack = new Map();//map to store timerObjects
function generateCoins(result, i) {
const timer = clearStack.get(i) || new timerObject(i);
clearStack.set(i, timer);
timer.stopTimer();
timer.startTimer(result);
}
class timerObject {
constructor(id) {
this.elementId = 'timer' + (id + 1);
this.timerIds = [];
}
startTimer(duration) {
const display = document.getElementById(this.elementId);
this.timerIds.push(
setInterval(() => {
const hours = parseInt(duration / 1000 / 60 / 60, 10),
minutes = parseInt((duration / 1000 / 60) % 60, 10),
seconds = parseInt((duration / 1000) % 60, 10);
display.innerHTML = [hours, minutes, seconds]
.map((e) => ('0' + e).slice(-2))
.join(':');
duration = duration - 1000;
if (duration < 0) this.stopTimer();
}, 1000)
);
}
stopTimer() {
this.timerIds.forEach(clearInterval);
this.timerIds = [];
}
}
document.addEventListener('DOMContentLoaded', function () {
setInterval(function () {
Promise.all(
coinBucket.map((bucket) => GSR('getCoins', bucket))
).then((values) => values.forEach(generateCoins));
}, 10000);
});
</script>
</html>
I have a function called ajaxCall() which simple does the call on a server and returns a value from a feed.
What I wanted to do is to save the value from feed to firstInterval variable, wait 10 seconds, make the call again and save the value to secondInterval variable. And then animate the increasing of these numbers on the webpage. This is what we have so far:
setInterval(function(){
getAmounts()
}, 11000);
function getAmounts(){
firstInterval = ajaxCall();
setTimeout(() => {
secondInterval = ajaxCall();
animateValue('#bronze-price p', firstInterval, secondInterval, 10000)
}, 10000);
};
function animateValue(id, start, end, duration) {
start = parseInt(start);
end = parseInt(end);
var range = end - start;
var current = start;
var increment = end > start? 1 : -1;
var stepTime = Math.abs(Math.floor(duration / range));
var obj = document.querySelector(id);
var timer = setInterval(function() {
current += increment;
obj.innerHTML = current;
if (current == end) {
clearInterval(timer);
}
}, stepTime);
}
So the idea is to have a function which gets first interval, after 10 seconds it grabs the second interval and calls the animation. This all is wrapped into setInterval function so I could change the number smoothly repeatedly.
However I am pretty sure that thats not a very clean solution since its setInterval in setTimeout and this all is wrapped in another setInterval function. I am also getting this kind of warnings in the console with both functions:
[Violation] 'setInterval' handler took ms
What would be the best approach to follow up on this idea but optimize the code?
I think Promises and requestAnimationFrame makes this a lot easier to handle while also getting rid of setTimeout/setInterval. An example would be:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_integer_between_two_values_inclusive
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// fake api endpoint
const ajaxCall = () => new Promise(resolve => {
setTimeout(resolve, 400, 1000 + getRandomIntInclusive(0, 1000));
});
// simple animation helper using requestAnimationFrame
const animate = (from, to, duration, cb) => {
const start = performance.now();
return new Promise(resolve => {
requestAnimationFrame(function loop() {
const f = (performance.now() - start) / duration;
cb(from + Math.round(f * (to - from)));
f < 1.0
? requestAnimationFrame(loop)
: resolve();
});
});
};
// main
(async (interval) => {
// where to output
const dst = document.querySelector('#out');
// initial value
let prev = 0;
// wait for next value of ajaxCall
while (true) {
const next = await ajaxCall();
console.log(`animating: ${prev} -> ${next}`);
// animate from `prev` to `next`
await animate(prev, next, interval, current => {
dst.innerHTML = current;
});
prev = next;
}
})(10000);
<div id="out"></div>
I am trying to make a function that will only retain its variable count from the past minute.
For example, if I call function count 100 times, if 60 of those calls were from the past minute, it will only return 60, and not 100.
This is what I have tried so far, making the variable an array and adding numbers to it everytime its called, but I do not know how to remove a count when a minute has passed.
var count = 1;
var countArray = [];
UPDATE:
I have updated my code with this, but the array elements are not removing as they should,
function Count() {
var jNow = new Date();
jCountArray.push(jNow);
for (var i = 0; i <= jCountArray.length; i++) {
var secondsDiff = (new Date(jNow).getTime() - new Date(jCountArray[i]).getTime()) / 1000;
if (secondsDiff >= 10) { jCountArray.shift(); }
}
console.log(jCountArray);
return jCountArray.length;
}
If I understand your requirements correctly, and the only thing you need Count to do is return how many times it was called in the last minute, I think we could do something relatively easily leveraging setTimeout:
function getCounter() {
let count = 0;
let duration = 60000;
return function () {
count = count + 1;
setTimeout(function () {
count = count - 1;
}, duration);
return count;
}
}
let counter = getCounter();
function count() {
console.log(counter());
}
<button onclick="count()">Count</button>
The only trick here is, to keep the actual count private, we need a factory function to wrap it in a closure. If that's unimportant you can write it as a simple function and keep the count in the parent scope. Also, for testing purposes you can change the duration to a lower millisecond count to see it in action.
I'm trying to display a second countdown after the first one finishes. I'm using meteor. This is the timer:
sec = 5
#timer = setInterval((->
$('#timer').text sec--
if sec == -1
$('#timer').fadeOut 'fast'
sec=
timer
return
), 1000)
This is how I call it
When the template is rendered I call a setTimeout and a countdown displays
Template.selector.rendered = ->
window.setTimeout(startGame, 5000)
timer
When game starts I need a second countdown. I managed it like this:
sec = 5
sw = 0
#timer = setInterval((->
$('#timer').text sec--
if sec == -1
if sw == 0
sw = 1
sec = 20
else if sw == 1
clearInterval timer
return
), 1000)
But there has to be a better way.
If you plan to use many timers, you could make an object to achieve that. Here is an example taken from here You could adapt it to your case using custom events:
ReactiveTimer = (function () {
// Constructor
function ReactiveTimer(interval) {
this._dependency = new Tracker.Dependency;
this._intervalId = null;
if(_.isFinite(interval))
this.start(interval);
};
ReactiveTimer.prototype.start = function(interval){
var _this = this;
this._intervalId = Meteor.setInterval(function(){
// rerun every "interval"
_this._dependency.changed();
}, 1000 * interval);
};
ReactiveTimer.prototype.stop = function(){
Meteor.clearInterval(this._intervalId);
this._intervalId = null;
};
ReactiveTimer.prototype.tick = function(){
this._dependency.depend();
};
return ReactiveTimer;
})();
First, Is it possible to find out
what is the monitor frame/refresh rate in
javascript (60Hz for most LCD monitors)?
Second, is there any way
to say execute a function after
every X frames?
Several people asked why I need this. Here is the context: I have an animation (an endless loop that renders one frame after another). The output of each iteration needs to be synchronized with monitor refresh rate, otherwise tearing will happen. The way I am doing it right now is to use setTimeout(loop, 16) within the loop method. Its sort of working. The second parameter needs to be 1/(refresh rate), and that is why I asked this question.
You may have some luck on modern browsers using window.requestAnimationFrame with a trivial callback that measures the time between successive invocations and from that calculate the FPS.
You should also be able to easily skip your render function every nth invocation to reduce the desired frame rate.
I put a rough example at http://jsfiddle.net/rBGPk/ - the math may be slightly wrong but it should be enough to show the general idea.
The solution below works by measuring the number of milliseconds between two consecutive animation frames.
Warning: It often returns an incorrect FPS because sometimes an animation frame is skipped when your CPU is busy with other tasks.
// Function that returns a Promise for the FPS
const getFPS = () =>
new Promise(resolve =>
requestAnimationFrame(t1 =>
requestAnimationFrame(t2 => resolve(1000 / (t2 - t1)))
)
)
// Calling the function to get the FPS
getFPS().then(fps => console.log(fps));
Tips
Do not abuse setInterval and setTimeout for real-time rendering
Instead use requestAnimationFrame
Review the MDN Tutorial on Timeouts and Intervals, particularly the section on requestAnimationFrame
This is robust method, using the requestAnimationFrame method.
function calcFPS(opts){
var requestFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame;
if (!requestFrame) return true; // Check if "true" is returned;
// pick default FPS, show error, etc...
function checker(){
if (index--) requestFrame(checker);
else {
// var result = 3*Math.round(count*1000/3/(performance.now()-start));
var result = count*1000/(performance.now()- start);
if (typeof opts.callback === "function") opts.callback(result);
console.log("Calculated: "+result+" frames per second");
}
}
if (!opts) opts = {};
var count = opts.count||60, index = count, start = performance.now();
checker();
}
The higher the value of count, the more accurate the value of the FPS, and the longer the FPS test will take.
Additional logic can be used to round to 15/12s, ie 24, 30, 48, 60 120... FPS.
Here's the compiled version (with rounding to 3 FPS):
function calcFPS(a){function b(){if(f--)c(b);else{var e=3*Math.round(1E3*d/3/(performance.now()-g));"function"===typeof a.callback&&a.callback(e);console.log("Calculated: "+e+" frames per second")}}var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;if(!c)return!0;a||(a={});var d=a.count||60,f=d,g=performance.now();b()}
Used like so:
calcFPS(); // Only logs to console (you can remove the console log,
// making this call redundant)
calcFPS({count: 30}); // Manually set count (the test should take 500ms
// on a 60FPS monitor
calcFPS({callback: useFPS}); // Specify a callback so you can use the
// FPS number value
var FPS = 0, err = calcFPS({count: 120, callback: fps => FPS = fps});
if (err) FPS = 30;
Calculate time interval between repaint:
const getRepaintInterval = () => {
return new Promise((resolve) => {
requestAnimationFrame((t1) => {
requestAnimationFrame((t2) => {
resolve(t2 - t1);
});
});
});
};
Or calculate FPS in the selected second:
const getFps = () => new Promise(resolve => {
let repaint = 0;
const start = performance.now();
const withRepaint = () => {
requestAnimationFrame(() => {
if ((performance.now() - start) < 1000) {
repaint += 1;
withRepaint();
} else {
resolve(repaint);
}
});
};
withRepaint();
});
Or calculate FPS with start and end:
const fpsHandler = () => {
let repaint;
let stop;
let ret;
let start;
const init = () => {
ret = undefined;
stop = false;
repaint = 0;
start = performance.now();
};
init();
const withRepaint = () => {
requestAnimationFrame(() => {
if (!stop) {
repaint += 1;
withRepaint();
}
});
};
return {
start: () => {
init();
withRepaint();
},
end: () => {
stop = true;
if (!ret) ret = repaint / ((performance.now() - start) / 1000);
return ret;
}
}
};
const { start, end } = fpsHandler();
As per some of the previous answer that explain to use requestAnimationFrame and measure the time difference between frames, here is a solution that uses requestAnimationFrame and the timestamp already sent to the callback. There is otherwise no reason to run a separate performance.now() function.
var previousTimestamp, divInterval, divFPS;
const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
const rafLoop = timestamp => {
let interval = timestamp - previousTimestamp;
let fps = 1000 / interval;
divInterval.innerHTML = `Interval: ${interval}`;
divFPS.innerHTML = `FPS: ${fps}`;
previousTimestamp = timestamp;
raf(rafLoop);
};
divInterval = document.getElementById('interval');
divFPS = document.getElementById('fps');
// This is run first to set the previousTimestamp variable with an initial value, and then call the rafLoop function.
raf(timestamp => {
previousTimestamp = timestamp;
raf(rafLoop);
});
<div id="interval"></div>
<div id="fps"></div>
See also https://codepen.io/sassano/pen/wvgxxMp for another sample with animation from which this snippet was derived.