Blocking Event Loop - javascript

I'm going through the "Functional Javascript Workshop" through Nodeschool. One of the exercises is titled "Blocking Event Loop" and I am having difficulty understanding it. With past exercises, I have made sure to really try to understand the solution so that if I were to have to redo the problem, I would understand how to solve it (as opposed to hacking away at it the first time). But this concept is really challenging me.
Modify the recursive repeat function provided in the boilerplate, such
that it does not block the event loop (i.e. Timers and IO handlers can
fire). This necessarily requires repeat to be asynchronous.
A timeout is queued to fire after 100 milliseconds, which will print the
results of the test and exit the process. repeat should release
control of the event loop to allow the timeout to interrupt before all the operations complete.
Try to perform as many operations as you can before the timeout fires!
Boilerplate
function repeat(operation, num) {
// modify this so it can be interrupted
if (num <= 0) return
operation()
return repeat(operation, --num)
}
module.exports = repeat
Solution
function repeat(operation, num) {
if (num <= 0) return
operation()
// release control every 10 or so
// iterations.
// 10 is arbitrary.
if (num % 10 === 0) {
setTimeout(function() {
repeat(operation, --num)
})
} else {
repeat(operation, --num)
}
}
module.exports = repeat
I have sought to understand setTimeout better and I somewhat understand how it works. But I do not understand some of the language and concepts in the question itself:
Modify the recursive repeat function provided in the boilerplate,
such that it does not block the event loop (i.e. Timers and IO handlers can fire). This necessarily requires repeat to be
asynchronous.
I don't understand how the solution, which makes every 10th repeat async, prevents repeat from blocking the event loop. I'm assuming the event loop and event queue are like the same things? For instance clicks get placed on the event queue while the javascript code is running synchronously through its code until the execution stack is empty and then the queue gets looked at. How does making every 10th repeat async prevent the queue from being blocked -- my understanding of "being blocked" is that items in the queue are blocked until the stack is empty, at which point javascript looks at what is in the queue. So lets say at repeat 10, 20, 30 etc, this code makes those repeats async, but doesn't it still continue through all the nums until it hits zero, at which point the stack is now empty, before javascript looks at the queue? So how does this solution resolve that blocking like the question asks?
Next, the question refers to "releasing control". No clue what that means. What is control and how is it released and what is it released to?
Immediately after that the question says "allow the timeout to interrupt before all the operations complete." Does interruption mean that basically every 10th repeat is interrupted (not allowed to complete until the synchronous code ends)?
Finally, the challenge at the end to try and perform as many operations as one can before the timeout fires... I also don't see how that applies to the solution. The setTimeout doesn't even seem to have a set duration.

The message queue (you refer to it as the event queue) is a list of messages to be processed
The event loop processes these messages one by one to completion
The blocking version of the repeat function you posted would be
function repeat(operation, num) {
if (num <= 0) return
operation()
repeat(operation, --num)
}
You can see that this recursively calls itself until num <= 0, so this wont complete until num <= 0, therefore no new messages will be processed by the event loop until this finishes
Contrast this with the non blocking version
function repeat(operation, num) {
if (num <= 0) return
operation()
// release control every 10 or so
// iterations.
// 10 is arbitrary.
if (num % 10 === 0) {
setTimeout(function() {
repeat(operation, --num)
})
} else {
repeat(operation, --num)
}
}
every 10 iterations, rather than recursively calling repeat, this function places (by virtue of the setTimeout) the next call to repeat to the end of the message queue and does nothing else (therefore is completed)
this allows the event loop to process any and all messages that were placed on the message queue during the 10 iterations of repeat before the setTimeout call, and only once those messages have been processed to completion will the callback in setTimeout be executed, which will be the next iteration of the repeat function

Related

Can something interrupt the timing of setTimeout?

How do you run a Javascript function in an interval that starts at the end of callback execution?
My post is a continuation of the previous question posted above. I was confused about setInterval. I wanted the interval to start after the time the previous execution of the function ends. I got a answer.
I started using setTimeout. Calling the function itself inside. It worked for a while.
Now occasionally there is some strange behaviour. I am struggling to diagnose it.
I am pasting my function at the bottom. You do not have to read all of it. The functions is running auditory recordings of digits and after issues a prompt, which stops execution and after the user answers there is a five second timeout after which the function is run again. More often than not it works fine, however some times after more execution it starts running closer and closer together, the timeout reduces from 5 seconds to almost nothing eventually. This does not happen in all contexts, so I do not think there is anything wrong in the function, which I posted. My question is what could change this schedule in the call stack? Some of the other code that is running might delay the start of the function(I could live with that), however the next call should still start 5000 ms after since it is executed after the audio ends.
I did not include all the other code that is running simultaneously, because there is simply too much of it.
function myTimer() {
var auditoryRoundNumber = gorilla.retrieve("auditoryRoundNumber", 0);
console.log("arn "+auditoryRoundNumber);
const queue = [];
var two_back=gorilla.retrieve("two_back", queue);
if(auditoryRoundNumber<2){
//console.log(auditoryRoundNumber);
randint= Math.floor(Math.random() * 10);
}else{
//after the first two trials we assign a higher probability to the digit that happen two trials ago.
//We make a draw in 50% of the cases it is a random digit in the other 50% it is the two back
var draw=Math.random();
if(draw>0.5){
randint= Math.floor(Math.random() * 10);
}else{
randint= two_back[0];
//console.log(randint);
}
}
two_back.push(randint);
randstimuli=gorilla.stimuliURL(mdict[randint]);
//console.log(randstimuli);
var audio = new Audio(randstimuli);
audio.play();
//var interval=5000;//(audioInterval=="Fixed") ? 5000 : Math.floor(Math.random() * 5000)+1000;
audio.onended=function(){
auditoryRoundNumber=auditoryRoundNumber+1;
gorilla.store("auditoryRoundNumber",auditoryRoundNumber);
//In the first two trial we do not ask what is the number two steps ago.
if(auditoryRoundNumber>2){
var start=Date.now();
var ans=prompt("Was the last number the same as the one two steps ago? Y-Yes/N-No");
ans=input_validation(ans);
//console.log(two_back.length);
ans=(ans=="y"||ans=="Y")? 1: 0;
//We remove the first item from the queue other items get reindexed
two_back_ans= two_back.shift();
//console.log(two_back_ans+" "+ (ans==two_back_ans)? 1:0);
gorilla.metric({
current_int: randint,
two_back_int: two_back_ans,
response_audio: ans,
audio_correct: (ans==(randint==two_back_ans))? 1:0,//expressions to evaluate similarity between ans and 2-back
audio_RT: Date.now()-start
});
//console.log(auditoryRoundNumber);
if(auditoryRoundNumber>= NoOfAudioTrials+2){
return;
}
}else{
gorilla.metric({
current_int: randint,
});
}
gorilla.store("two_back", two_back);
task_id=setTimeout(myTimer,5000);
}
}

Update webpage to show progress while javascript is running in in a loop

I have written javascript that takes 20-30 seconds to process and I want to show the progress by updating the progress bar on my webpage.
I have used setTimeout in an attempt to allow webpage to be re-drawn.
This is how my code looks like:
function lengthyFun(...){
for(...){
var progress = ...
document.getElementById('progress-bar').setAttribute('style',"width:{0}%".format(Math.ceil(progress)));
var x = ...
// Processing
setTimeout(function(x) { return function() { ... }; }(x), 0);
}
}
It does not work, I know why it does not work, but I don't know how to refactor my code to make it work.
As you probably know, the problem here is that you main process (the one that takes a lot of time), is blocking any rendering. That's because JavaScript is (mostly) mono-threaded.
From my point of view, you have two solutions to do this.
The first one is to cut down your main process into different parts and to do the rendering between each of them. I.e. you could have something like that (using Promises) :
var processParts = [/* array of func returning promises */];
function start(){
// call the first process parts
var firstPartPromise = (processParts.shift())();
// chain it with all the other process parts interspersed by updateDisplay
return processParts.reduce(function(prev, current){
return val.then(current).then(updateDisplay);
}, firstPartPromise);
}
You will probably need a polyfill for the promises (one here). If you use jQuery, they have a (bad non standard) implementation.
The second solution can be to use webworkers which allows you to create threads in JavaScript. It works on all modern browsers.
It is probably the best solution in your case.
I never used them but you are supposed to be able to do stuff like:
var process = new Worker("process.js");
worker.onmessage(function(event){
updateProgress(event.data.progress)
});
And the in process.js:
postMessage({progress: 0.1});
// stuff
postMessage({progress: 0.4});
// stuff
postMessage({progress: 0.7});
//etc
Try setting progress element attribute min to 0 , max to 20000 , value to 0 ; create function where if value less than max increment value by 1000 ; utilize setTimeout with duration set to 1000 to call function recursively until value reaches max
var p = document.querySelector("progress");
function redraw() {
if (p.value < p.max) {
p.value += 1000;
setTimeout("redraw()", 1000)
}
}
redraw()
<progress max="20000" min="0" value="0"></progress>
There are a couple of ways that I know of to trigger sequential HTML redraws through Javascript:
Incremental Timeout Period
Recursive Method Calls
The first and easiest way of doing this is by using a multiplier (such as the iterator) on the timeout interval in a loop. This method should be sufficient if the operation is independent of external variables and only needs to be run a finite and relatively few number of times. The more operations required/likely to occur, the greater the strain on resources - just for calculating intervals. Another drawback takes effect when the processing time exceeds the timeout interval, causing a knock-on to the interval of the observed redraws. The result of this can be that the web page freezes up entirely until all operations are done.
Example
for (var i=0, limit=n; i<limit; i++) {
setTimeout((function(params) {
return function() {
some_func(params);
}
})(param_values), i*1000);
}
The second method is a little more convoluted, but guarantees redraws between each operation, regardless of the timeout interval. Here, the timeout only affects the time between redraws ands resists the effects of consecutive operation variables. However, the processing time for the current operation is still a factor for the observed interval and will still freeze up a web page between redraws if the operation is computationally intensive.
Example
var limit = n;
var i = 0;
recursive_timeout();
function recursive_timeout() {
setTimeout((function(params) {
return function() {
some_func(params);
i++;
if (i<limit) {
recursive_timeout();
}
}
})(param_values, i, limit), 1000);
}
Refined Example (based off guest271314's answer)
var still_true = true;
recursive_timeout();
function recursive_timeout() {
some_func(params);
if (still_true) {
setTimeout(function() {recursive_timeout();}, 1000);
}
}
While the incremental method is fine for simple tasks, recursion will reliably perform redraws. If long processing times per operation is an issue, then it might be worth delving into asynchronous tasks in addition to using recursion in order to avoid rendering a web page temporarily unusable.
Anyway, hope this helps!
Ha! Just realised guest271314 put up a much more elegant example of the recursive method... Oh well, more info can't hurt.

Async behaviour in javascript [duplicate]

This question already has answers here:
Is JavaScript guaranteed to be single-threaded?
(13 answers)
Closed 8 years ago.
Consider the following javascript algorithm:
for(var i = 0; i < 50; i ++){
console.log('starting :', i);
getPrimes(1000000);
(function(i){
setTimeout(function(){
console.log('done :', i);
}, 1000);
})(i);
}
function getPrimes(max) {
var sieve = [], i, j, primes = [];
for (i = 2; i <= max; ++i) {
if (!sieve[i]) {
// i has not been marked -- it is prime
primes.push(i);
for (j = i << 1; j <= max; j += i) {
sieve[j] = true;
}
}
}
return primes;
}
The for loop launches getPrimes function that takes some time and then runs another function that has timeout of one second. Thinking about this algorithm what I will be expecting to get on the console is a mix between "starting" and "done" lines ordered from start to end but not necessarily sorted from 1 to 50.
In reality what I get is 50 lines of "starting" (that take some time to process) and then immidiatelly 50 lines of "done".
My question is why? The logic tells that while running the for loop at least one timout callback should be finished and because javascript is single threaded asynchronous it should display a line of "done" in between some lines of "starting"
Could it be because the primes calculation takes all the cpu power?
Thanks
The problem lies within javascrypt events array, so all the timeout functions runs are getting into array, and javascript virtual machine runs them after for loop finishes his run. For better understanding ypu can run this code:
for( var i=0; i<10; i++) {
console.log(i);
setTimeout(function(){
console.log('hhh');
},0)
}
So the output will be all the before setTimeout console.logs and then all the setTimeout's console.log.
When we call setTimeout, a timeout event is queued. Then execution continues:
the line after the setTimeout call runs, and then the line after that, and so on,
until there are no lines left. Only then does the JavaScript virtual machine
ask, “What’s on the queue?”
If there’s at least one event on the queue that’s eligible to “fire” (like a 500ms
timeout that was set 1000ms ago), the VM will pick one and call its handler
(e.g., the function we passed in to setTimeout). When the handler returns, we
go back to the queue.
Input events work the same way: when a user clicks a DOM element with a
click handler attached, a click event is queued. But the handler won’t be
executed until all currently running code has finished (and, potentially, until
after other events have had their turn). That’s why web pages that use Java-
Script imprudently tend to become unresponsive.
In your specific problem they got fired all together just because you put them into "events array" all together, so their time are pretty same for all of them, and the time you waiting before is just the time you set in setTimeout+minor delta of calculation time.
While programming in Angular, I used this to ensure that my code runs after anything else finished, so I just put setTimeout() without time value, and inside code was executed right after everything else.
Javascript is not multithreaded: the code will continue to execute until it returns to the main loop.
The loop occurs 50 times displaying starting
Then in each loop code (function(){})(); is executed as soon as it is read since it is self-invoking!
but the setTimeout will execute after for loop
for-loop timer: timer started
"starting :" 0
selfinvokingfunction timer: timer started
selfinvokingfunction timer: 0ms
"starting :" 1
selfinvokingfunction timer: timer started
selfinvokingfunction timer: 0ms
"starting :" 2
selfinvokingfunction timer: timer started
selfinvokingfunction timer: 0ms
for-loop timer: 656ms
setTimeout timer: timer started
"done :" 0
setTimeout timer: 0ms
setTimeout timer: timer started
"done :" 1
setTimeout timer: 0ms
setTimeout timer: timer started
"done :" 2
setTimeout timer: 0ms
Used console.time and console.timeEnd to check timing the execution sequence is same, whether for loop finishes withing a second or not.

Timer with new Date() in js

I got 5 sprites( 5 blocks, each next smaller than previous).
And they drawing too fast. how to right use new Date(), getMilliseconds or other methods for timer that would draw with some delay?
For ex.
for ( i = 0; i < 5; i ++ ) {
xxx.drawImage(sprite, i, etc);
}
Use setInterval() or setTimeout() to set a timer. But you should notice a subtle difference between these 2 functions due to single threaded nature of javascript. Let's look at the example:
setTimeout(function test() {
//your code
setTimeout(test, 100);
}, 100);
setInterval(function() {
//your code
}, 100);
These 2 blocks of code appear to be the same, but that's not the case due to single-threaded nature of javascript.
With setInterval(): if your code is busy executing and takes long time to complete. The next intervals may fire while your code is still executing and these intervals will be queued up. In the end, you may end up with some handlers running in succession without any delay
With setTimeout(): the next interval is only set to be fired when you're current code has finished. This guarantee that the next execution will never be less than the interval (100 in this case), only equal to or longer.

setTimeout or setInterval?

As far as I can tell, these two pieces of javascript behave the same way:
Option A:
function myTimeoutFunction()
{
doStuff();
setTimeout(myTimeoutFunction, 1000);
}
myTimeoutFunction();
Option B:
function myTimeoutFunction()
{
doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
Is there any difference between using setTimeout and setInterval?
They essentially try to do the same thing, but the setInterval approach will be more accurate than the setTimeout approach, since setTimeout waits 1000ms, runs the function and then sets another timeout. So the wait period is actually a bit more than 1000ms (or a lot more if your function takes a long time to execute).
Although one might think that setInterval will execute exactly every 1000ms, it is important to note that setInterval will also delay, since JavaScript isn't a multi-threaded language, which means that - if there are other parts of the script running - the interval will have to wait for that to finish.
In this Fiddle, you can clearly see that the timeout will fall behind, while the interval is almost all the time at almost 1 call/second (which the script is trying to do). If you change the speed variable at the top to something small like 20 (meaning it will try to run 50 times per second), the interval will never quite reach an average of 50 iterations per second.
The delay is almost always negligible, but if you're programming something really precise, you should go for a self-adjusting timer (which essentially is a timeout-based timer that constantly adjusts itself for the delay it's created)
Is there any difference?
Yes. A Timeout executes a certain amount of time after setTimeout() is called; an Interval executes a certain amount of time after the previous interval fired.
You will notice the difference if your doStuff() function takes a while to execute. For example, if we represent a call to setTimeout/setInterval with ., a firing of the timeout/interval with * and JavaScript code execution with [-----], the timelines look like:
Timeout:
. * . * . * . * .
[--] [--] [--] [--]
Interval:
. * * * * * *
[--] [--] [--] [--] [--] [--]
The next complication is if an interval fires whilst JavaScript is already busy doing something (such as handling a previous interval). In this case, the interval is remembered, and happens as soon as the previous handler finishes and returns control to the browser. So for example for a doStuff() process that is sometimes short ([-]) and sometimes long ([-----]):
. * * • * • * *
[-] [-----][-][-----][-][-] [-]
• represents an interval firing that couldn't execute its code straight away, and was made pending instead.
So intervals try to ‘catch up’ to get back on schedule. But, they don't queue one on top of each other: there can only ever be one execution pending per interval. (If they all queued up, the browser would be left with an ever-expanding list of outstanding executions!)
. * • • x • • x
[------][------][------][------]
x represents an interval firing that couldn't execute or be made pending, so instead was discarded.
If your doStuff() function habitually takes longer to execute than the interval that is set for it, the browser will eat 100% CPU trying to service it, and may become less responsive.
Which do you use and why?
Chained-Timeout gives a guaranteed slot of free time to the browser; Interval tries to ensure the function it is running executes as close as possible to its scheduled times, at the expense of browser UI availability.
I would consider an interval for one-off animations I wanted to be as smooth as possible, whilst chained timeouts are more polite for ongoing animations that would take place all the time whilst the page is loaded. For less demanding uses (such as a trivial updater firing every 30 seconds or something), you can safely use either.
In terms of browser compatibility, setTimeout predates setInterval, but all browsers you will meet today support both. The last straggler for many years was IE Mobile in WinMo <6.5, but hopefully that too is now behind us.
setInterval()
setInterval() is a time interval based code execution method that has the native ability to repeatedly run a specified script when the interval is reached. It should not be nested into its callback function by the script author to make it loop, since it loops by default. It will keep firing at the interval unless you call clearInterval().
If you want to loop code for animations or on a clock tick, then use setInterval().
function doStuff() {
alert("run your code here when time interval is reached");
}
var myTimer = setInterval(doStuff, 5000);
setTimeout()
setTimeout() is a time based code execution method that will execute a script only one time when the interval is reached. It will not repeat again unless you gear it to loop the script by nesting the setTimeout() object inside of the function it calls to run. If geared to loop, it will keep firing at the interval unless you call clearTimeout().
function doStuff() {
alert("run your code here when time interval is reached");
}
var myTimer = setTimeout(doStuff, 5000);
If you want something to happen one time after a specified period of time, then use setTimeout(). That is because it only executes one time when the specified interval is reached.
The setInterval makes it easier to cancel future execution of your code. If you use setTimeout, you must keep track of the timer id in case you wish to cancel it later on.
var timerId = null;
function myTimeoutFunction()
{
doStuff();
timerId = setTimeout(myTimeoutFunction, 1000);
}
myTimeoutFunction();
// later on...
clearTimeout(timerId);
versus
function myTimeoutFunction()
{
doStuff();
}
myTimeoutFunction();
var timerId = setInterval(myTimeoutFunction, 1000);
// later on...
clearInterval(timerId);
I find the setTimeout method easier to use if you want to cancel the timeout:
function myTimeoutFunction() {
doStuff();
if (stillrunning) {
setTimeout(myTimeoutFunction, 1000);
}
}
myTimeoutFunction();
Also, if something would go wrong in the function it will just stop repeating at the first time error, instead of repeating the error every second.
The very difference is in their purposes.
setInterval()
-> executes a function, over and over again, at specified time intervals
setTimeout()
-> executes a function, once, after waiting a specified number of milliseconds
It's as simple as that
More elaborate details here http://javascript.info/tutorial/settimeout-setinterval
When you run some function inside setInterval, which works more time than timeout-> the browser will be stuck.
- E.g., doStuff() takes 1500 sec. to be execute and you do: setInterval(doStuff, 1000);
1) Browser run doStuff() which takes 1.5 sec. to be executed;
2) After ~1 second it tries to run doStuff() again. But previous doStuff() is still executed-> so browser adds this run to the queue (to run after first is done).
3,4,..) The same adding to the queue of execution for next iterations, but doStuff() from previous are still in progress...
As the result- the browser is stuck.
To prevent this behavior, the best way is to run setTimeout inside setTimeout to emulate setInterval.
To correct timeouts between setTimeout calls, you can use self-correcting alternative to JavaScript's setInterval technique.
Your code will have different execution intevals, and in some projects, such as online games it's not acceptable. First, what should you do, to make your code work with same intevals, you should change "myTimeoutFunction" to this:
function myTimeoutFunction()
{
setTimeout(myTimeoutFunction, 1000);
doStuff();
}
myTimeoutFunction()
After this change, it will be equal to
function myTimeoutFunction()
{
doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
But, you will still have not stable result, because JS is single-threaded. For now, if JS thread will be busy with something, it will not be able to execute your callback function, and execution will be postponed for 2-3 msec. Is you have 60 executions per second, and each time you have random 1-3 sec delay, it will be absolutely not acceptable (after one minute it will be around 7200 msec delay), and I can advice to use something like this:
function Timer(clb, timeout) {
this.clb = clb;
this.timeout = timeout;
this.stopTimeout = null;
this.precision = -1;
}
Timer.prototype.start = function() {
var me = this;
var now = new Date();
if(me.precision === -1) {
me.precision = now.getTime();
}
me.stopTimeout = setTimeout(function(){
me.start()
}, me.precision - now.getTime() + me.timeout);
me.precision += me.timeout;
me.clb();
};
Timer.prototype.stop = function() {
clearTimeout(this.stopTimeout);
this.precision = -1;
};
function myTimeoutFunction()
{
doStuff();
}
var timer = new Timer(myTimeoutFunction, 1000);
timer.start();
This code will guarantee stable execution period. Even thread will be busy, and your code will be executed after 1005 mseconds, next time it will have timeout for 995 msec, and result will be stable.
I use setTimeout.
Apparently the difference is setTimeout calls the method once, setInterval calls it repeatdly.
Here is a good article explaining the difference: Tutorial: JavaScript timers with setTimeout and setInterval
I've made simple test of setInterval(func, milisec), because I was curious what happens when function time consumption is greater than interval duration.
setInterval will generally schedule next iteration just after the start of the previous iteration, unless the function is still ongoing. If so, setInterval will wait, till the function ends. As soon as it happens, the function is immediately fired again - there is no waiting for next iteration according to schedule (as it would be under conditions without time exceeded function). There is also no situation with parallel iterations running.
I've tested this on Chrome v23. I hope it is deterministic implementation across all modern browsers.
window.setInterval(function(start) {
console.log('fired: ' + (new Date().getTime() - start));
wait();
}, 1000, new Date().getTime());
Console output:
fired: 1000 + ~2500 ajax call -.
fired: 3522 <------------------'
fired: 6032
fired: 8540
fired: 11048
The wait function is just a thread blocking helper - synchronous ajax call which takes exactly 2500 milliseconds of processing at the server side:
function wait() {
$.ajax({
url: "...",
async: false
});
}
Both setInterval and setTimeout return a timer id that you can use to cancel the execution, that is, before the timeouts are triggered. To cancel you call either clearInterval or clearTimeout like this:
var timeoutId = setTimeout(someFunction, 1000);
clearTimeout(timeoutId);
var intervalId = setInterval(someFunction, 1000),
clearInterval(intervalId);
Also, the timeouts are automatically cancelled when you leave the page or close the browser window.
To look at it a bit differently: setInterval ensures that a code is run at every given interval (i.e. 1000ms, or how much you specify) while setTimeout sets the time that it 'waits until' it runs the code. And since it takes extra milliseconds to run the code, it adds up to 1000ms and thus, setTimeout runs again at inexact times (over 1000ms).
For example, timers/countdowns are not done with setTimeout, they are done with setInterval, to ensure it does not delay and the code runs at the exact given interval.
You can validate bobince answer by yourself when you run the following javascript or check this JSFiddle
<div id="timeout"></div>
<div id="interval"></div>
var timeout = 0;
var interval = 0;
function doTimeout(){
$('#timeout').html(timeout);
timeout++;
setTimeout(doTimeout, 1);
}
function doInterval(){
$('#interval').html(interval);
interval++;
}
$(function(){
doTimeout();
doInterval();
setInterval(doInterval, 1);
});
Well, setTimeout is better in one situation, as I have just learned. I always use setInterval, which i have left to run in the background for more than half an hour. When i switched back to that tab, the slideshow (on which the code was used) was changing very rapidly, instead of every 5 seconds that it should have. It does in fact happen again as i test it more and whether it's the browser's fault or not isn't important, because with setTimeout that situation is completely impossible.
The difference is obvious in console:
Just adding onto what has already been said but the setTimeout version of the code will also reach the Maximum call stack size which will stop it from functioning. Since there is no base case for the recursive function to stop at so you can't have it run forever.
If you set the interval in setInterval too short, it may fire before the previous call to the function has been completed. I ran into this problem with a recent browser (Firefox 78). It resulted in the garbage collection not being able to free memory fast enough and built up a huge memory leak.
Using setTimeout(function, 500); gave the garbage collection enough time to clean up and keep the memory stable over time.
Serg Hospodarets mentioned the problem in his answer and I fully agree with his remarks, but he didn't include the memory leak/garbage collection-problem. I experienced some freezing, too, but the memory usage ran up to 4 GB in no time for some minuscule task, which was the real bummer for me. Thus, I think this answer is still beneficial to others in my situation. I would have put it in a comment, but lack the reputation to do so. I hope you don't mind.
The reason why Option A and Option B seem like they work the same is mostly because the places of the setInterval and the setTimeout functions.
function myTimeoutFunction()
{
doStuff();
setTimeout(myTimeoutFunction, 1000);
}
myTimeoutFunction();
This one is a recursive function, and if doStuff is very complex, setTimeout has to keep track of all calls of the setTimout plus the current doStuff, which makes it become slower and s l o w e r.
function myTimeoutFunction()
{
doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
On the other hand, the setInterval only has to keep track of the last setInterval and the current doStuff, making it staying at a constant speed.
So which one should you use?
From the above, you should probably be able to conclude that the better one is setInterval.
The important point to consider is the performance.
The only way to run a function periodically using setTimeout is to call it recursively with the target function, and when you check it, it appears that it works asynchronously put when you see the call stack you will find it keep growing by the time. In fact, it is sensible. Since Javascript does not support multi-threading, it is impossible to finish calling the parent function before finishing the child function, therefor, the stack will keep growing as long as there is recursive calling.
Whilst, with setInterval we don't need to call the target function recursively since it has a logic that runs it periodically as a loop. So, this keeps the call stack clean.
You can watch the call stack using developer's tools in your browser and you will notice the difference.
The difference will be clear when using small interval for a long period of time.
I think SetInterval and SetTimeout are different. SetInterval executes the block according to the time set while, SetTimeout executes the block of code once.
Try these set of codes after the timeout countdown seconds:
setInterval(function(e){
alert('Ugbana Kelvin');
}, 2000);
and then try
setTimeout(function(e){
alert('Ugbana Kelvin');
}, 2000);
You can see the differences for yourself.

Categories

Resources