Javascript: Multiple timers VS single timer - javascript

I have a simple question about Javascript timers and their performance but it really bugs me because I can't figure out the right way to do it.
Here is the problem: I have a special timing function that self corrects itself:
function setCorrectingInterval(func, delay) {
if (!(this instanceof setCorrectingInterval)) {
return new setCorrectingInterval(func, delay);
}
var target = Date.now() + delay;
var self = this;
function tick() {
if (self.stopped) {
return;
}
target += delay;
func();
setTimeout(tick, target - Date.now());
}
setTimeout(tick, delay);
}
Then, in my application I use this function n times to set n different timer variables. So if I have two timer variables I would use this function twice to set them. In the code bellow I execute first task every 1000ms and second task every 2000ms:
var firstTimer,
secondTimer;
function startFirstTimer() {
firstTimer = setCorrectingInterval(function() {
console.log("First!");
}, 1000);
}
function startSecondTimer() {
secondTimer = setCorrectingInterval(function() {
console.log("Second!");
}, 2000);
}
startFirstTimer();
startSecondTimer();
In my angular application the need for multiple timing events grows and grows. So my (tempoprary) solution to this is to create new timer variables with new setTimeouts using the setCorrectingInterval function as mentioned above.
So I got thinking - wouldn't it be better (performance wise) to just have one single timer variable with one single setTimeout in which using if statements I would execute tasks that meet the condition, like this:
var timer;
function startTimer() {
var counter = 1;
timer = setCorrectingInterval(function() {
console.log("First!");
if (counter % 2 === 0) {
console.log("Second!");
}
counter++;
}, 1000);
}
startTimer();
( provided every tasks would repeat execution with delay that is multiple of 1000 )
Which approach is better and why? Any explanation is very much appreciated, thank you

Related

Can you update setInterval variable while it runs

I need to modify existing slider. Its slides now have differing data-seconds added to it and need be active that long. Previously I had:
var slidePause = 10;
function startSlideBanner() {
bannerTimer = setInterval(nextSlide, slidePause * 1000);
}
startSlideBanner();
Which worked infinitely well. Now I would need to update slidePause variable every iteration. Looking for an example if its possible.
No: You cannot do it with setInterval. Once it is set, it may only be cancelled.
What you can do however, is use setTimeout to achieve your goals. While this can be done recursively, I prefer to take advantage of promises to do it iteratively:
const wait = ms => new Promise(res => setTimeout(res, ms));
let slidePause = 10;
async function startSlideBanner() {
while(true) {
await wait(slidePause * 1000);
nextSlide();
// Example: Double the time for each slide
slidePause = slidePause * 2;
}
}
startSlideBanner();
One of the problems with setInterval() is that JavaScript's single threaded nature can result in uneven periods between the setInterval() code being fired. To avoid this, run setInterval() at a faster rate, and calculate the time passed to determine whether an action should be taken.
If you make the calculation for time passed dependent on a variable you can change the effective rate at which the event occurs.
function nextSlide(period){
console.log("Next Slide in "+period);
}
function VariableTimer(period, startImmediately = true) {
this.period = period;
self = this;
this.startTime = startImmediately?0:Date.now();
this.time = setInterval(function(){
if (Date.now()-self.startTime > self.period*1000) {
self.startTime = Date.now();
nextSlide(self.period);
}
}, 100); // Run setInterval at 100ms intervals.
this.stop = function(){
clearInterval(self.time);
}
}
let timer = new VariableTimer(10);
// Change the timer period like this
// timer.period = 5;
// After 20 seconds switch to 5 second intervals
setTimeout(function(){timer.period = 5;},20000);
// After 40 seconds, stop the timer.
setTimeout(function(){timer.stop();console.log("timer stopped")}, 40000);

How to stop executinf function after certain amount of time in JavaScript

function avoidAfterTime() {
var startTime = new Date().getTime();
var interval = setInterval(function () {
if (new Date().getTime() - startTime > 3000) {
clearInterval(interval);
console.log("more than 2 sec")
return;
}
longWorking();
}, 2000);
}
avoidAfterTime();
function longWorking(){
var t;
for (i = 0; i < 1e10; i++) t = i;
console.log(t);
}
Hello. I am very new to JS. But I need to stop running some function (here it is longWorking) which can be executed for few seconds or for so much time. And I want to abort the function in case of it takes too long. I guess I know how to make it using, for example, threads in some other programming language. But I have no idea about making it in JS. I thought in this way (above)... But it doesn't work. Can someone help me?
hmm, I drafted this example. So it's a function that runs every second and if it takes more than 6 seconds it will stop. So basically you can put your work load in the doSomething() function and let it work every second and stop it if it takes too long. Or you can stop it based on a value. It depends on what do you want to do with it. I used the module pattern to isolate the logic and the variables. So you can encapsulate your logic in a module like way.
(function() {
'use strict';
let timing = 0;
const interval = setInterval(doSomething, 1000);
function doSomething() {
timing++;
if (timing > 5) {
clearInterval(interval);
}
console.log('working');
}
})();
Is this something you are looking for?

Jquery - looping with nested functions and setInterval()

Probably not the clearest title, but here goes - I need to display two independent countdowns on a page, accepting a user input for the starting value for each. When one reaches zero, the other starts and counts down to zero. Code for this is below, and working as expected.
I call the Timer1 function, which checks a variable for the starting value, if it exists, the count starts. When the count is zero, I clear the interval, reset the display to the starting value, and fire the second timer, if it has a value assigned:
function Timer1() {
var gDuration = goTime;
countdown = setInterval(function () {
if (gDuration >= 0) {
$("#durationValue").html(ToTime(gDuration));
gDuration--;
}
else {
clearInterval(countdown);
$("#durationValue").html(ToTime(goTime));
if (restTime != 0) {
Timer2();
}
}
}, 1000);
}
function Timer2() {
var rDuration = restTime;
countdown = setInterval(function () {
if (rDuration >= 0) {
$("#restValue").html(ToTime(rDuration));
rDuration--;
}
else {
clearInterval(countdown);
$("#restValue").html(ToTime(restTime));
}
}, 1000);
}
The next step is to allow that process to run for a set number of loops - I've tried wrapping setInterval in Timer1 in a for loop, which doesn't work. Any ideas how to better go about this?
for-loops don't work well with asynchronous stuff. Just make it a counter with an end condition as you have demonstrated with g/rDuration already.
With some callback abstractions, and heavy continuation-passing-style:
function timer(el, duration, interval, callback) {
var countdown = setInterval(function() {
if (duration-- >= 0) {
el.text(ToTime(duration));
} else {
clearInterval(countdown);
callback();
}
}, interval);
}
var goTime = …, restTime = …;
function t1(cb) {
timer($("#durationValue"), goTime, 1000, cb);
}
function t2(cb) {
timer($("#restValue"), restTimer, 1000, cb);
}
var loops = …;
(function loop(cb) {
t1(function(){
t2(function() {
if (loop-- >= 0)
loop(cb);
else
cb();
});
});
})(function() {
alert("finished!");
});
The easiest thing I can think of is to have your Timer functions have a parameter with the current iteration. Increment that value whenever one timer starts another time. And use that value to determine if it should indeed start the next timer.

Javascript countdown is getting faster after each run

var i = 3400;
function progress() {
i = 34000;
window.setInterval(function () {
i = i - 100;
document.getElementById("progress").firstChild.data = i;
}, 100);
}
This code is getting faster and faster. The function progress is called every 3 seconds, but I can't change the it's called because it's event based. After around 10 calls i is getting negative!
Umm....
Do not use setInterval
You probably want to use setTimeout
Since progress is called every 3 seconds, you need to avoid that it creates new intervals repeatedly. Using clearTimeout resets the timer anytime you call progress. However, without knowing what exactly you want to achive it's difficult to provide an accurate answer.
var timeout;
function counter(count) {
document.getElementById("progress").firstChild.data = count;
if (count >= 0) {
timeout = window.setTimeout(function() {
counter(count-100);
}, 100);
}
}
function progress() {
window.clearTimeout(timeout);
counter(3400);
}
Try this
var i = 3400;
function progress() {
i = i - 100;
document.getElementById("progress").firstChild.data = i;
window.setTimeout('progress();', 100);
}

How can I show a list of every thread running spawned by setTimeout/setInterval

I want to do this either by pure javascript or any sort of console in a browser or whatever.
Is it possible?
Thanks
Further explanations:
I want to debug a library that does animations. I want to know if there's multiple timers created if there are multiple objects being animated.
Note that setTimeout() does not spawn new threads. Browser side scripting is not only single threaded, but the JavaScript evaluation shares the same single thread with the page rendering (Web Workers apart).
Further reading:
How JavaScript Timers Work by John Resig
You may want to build a timer manager yourself:
var timerManager = (function () {
var timers = [];
return {
addTimer: function (callback, timeout) {
var timer, that = this;
timer = setTimeout(function () {
that.removeTimer(timer);
callback();
}, timeout);
timers.push(timer);
return timer;
},
removeTimer: function (timer) {
clearTimeout(timer);
timers.splice(timers.indexOf(timer), 1);
},
getTimers: function () {
return timers;
}
};
})();
Then use it as follows:
var t1 = timerManager.addTimer(function () {
console.log('Timer t1 triggered after 1 second');
}, 1000);
var t2 = timerManager.addTimer(function () {
console.log('Timer t2 triggered after 5 second');
console.log('Number of Timers at End: ' + timerManager.getTimers().length);
}, 5000);
console.log('Number of Timers at Start: ' + timerManager.getTimers().length);
The above will display the following result in the console:
// Number of Timers at Start: 2
// Timer t1 triggered after 1 second
// Timer t2 triggered after 5 second
// Number of Timers at End: 0
Note that the timerManager implementation above uses the Array.indexOf() method. This has been added in JavaScript 1.6 and therefore not implemented by all browsers. However, you can easily add the method yourself by adding the implementation from this Mozilla Dev Center article.
Finally done, it was interesting for me so I spent some time trying to come up with something, and here it's
It overrides browser's setTimeout and fill active status of current active calls in window._activeSetTimeouts hash, with window._showCurrentSetTimeouts() demo function that displays current setTimeout calls that are waiting.
if(typeof window._setTimeout =='undefined') {
window._setTimeout=window.setTimeout;
window._activeSetTimeouts={};
window._activeSetTimeoutsTotal=0;
window._setTimeoutCounter=0;
window._showCurrentSetTimeouts=function() {
var tgt=document.getElementById('_settimtouts');
if(!tgt) {
tgt=document.createElement('UL');
tgt.style.position='absolute';
tgt.style.border='1px solid #999';
tgt.style.background='#EEE';
tgt.style.width='90%';
tgt.style.height='500px';
tgt.style.overflow='auto';
tgt.id='_settimtouts';
document.body.appendChild(tgt);
}
tgt.innerHTML='';
var counter=0;
for(var i in window._activeSetTimeouts) {
var li=document.createElement('LI');
li.innerHTML='[{status}] {delay} ({calltime})<br /><pre style="width: 100%; height: 5em; overflow: auto; background: {bgcolor}">{cb}</pre>'.f(window._activeSetTimeouts[i]);
li.style.background=(counter++%2)?'#CCC' : '#EEB';
tgt.appendChild(li);
}
}
window.setTimeout=function(cb, delay) {
var id = window._setTimeoutCounter++;
var handleId = window._setTimeout(function() {
window._activeSetTimeouts[id].status='exec';
cb();
delete window._activeSetTimeouts[id];
window._activeSetTimeoutsTotal--;
}, delay);
window._activeSetTimeouts[id]={
calltime:new Date(),
delay:delay,
cb:cb,
status:'wait'
};
window._activeSetTimeoutsTotal++;
return id;
}
//the following function is for easy formatting
String.prototype.f=function(obj) {
var newStr=this+'';
if(arguments.length==1) {
if(typeof(obj)=='string') {
obj={x:obj};
}
for(var i in obj) {
newStr=newStr.replace(new RegExp('{'+i+'}', 'g'), obj[i]+'');
}
newStr+='';
} else {
for(var i=0; i<arguments.length; i++) {
newStr=newStr.replace('{'+(i+1)+'}', arguments[i]);
}
}
return newStr;
}
}
//following line for test
for(var i=0; i<5; i++) setTimeout(window._showCurrentSetTimeouts, 3000*i);
As others have mentioned, setTimeout doesn’t spawn a thread. If you want a list of all the timeout ids (so you can cancel them, for example) then see below:
I don’t think you can get a list of all timeout ids without changing the code when they are called. setTimeout returns an id—and if you ignore it, then it's inaccessible to your JavaScript. (Obviously the interpreter has access to it, but your code doesn't.)
If you could change the code you could do this:
var timeoutId = [];
timeoutId.push(setTimeout(myfunc, 100));
…Making sure that timeoutId is declared in global scope (perhaps by using window.timeoutId = []).
Just off the top of my head, but to reimplement setTimeout you’d have to do something like this:
var oldSetTimeout = setTimeout;
setTimeout = function (func, delay) {
timeoutId.push(oldSetTimeout(func, delay));
}
This isn’t tested, but it gives you a starting point. Good idea, molf!
Edit: aularon's answer gives a much more thorough implementation of the above idea.

Categories