How does single threading block DOM manipulation in JavaScript? - javascript

I hope my title is correct. I was at an interview today and was stumped by a question dealing with the event loop/ single threading. Here is the code:
function longCB(){
for (var i = 0; i <= 9999; i++){
console.log(i);
}
$('#status').text('hello')
}
$('#status').text('hi')
longCB();
The question was why does the DOM never display 'hi' on the #status div?
Being completely stumped the interviewer explained it was because of single threading in javascript and explained that just by adding a setTimeout to longCB() even if it was set to 0 would make it work. After reviewing the event loop/ event que and call stacks I am still completely stumped why this doesn't work? Shouldn't the line
$('#status').text('hi')
be done before even calling longCB()? What am I missing? Thank you in advanced.

That's because the single thread is also shared by the browser's rendering engine. As long as JS is running synchronously, the page on the screen doesn't get updated. Why? Well, say you had this code:
elem.style.color = 'red';
elem.style.opacity = '1';
elem.textContent = 'Hello';
You wouldn't want the element to show the old text in red, then make it more opaque and then change the text to Hello. You just want the text to immediately change and be red and opaque. This is one reason why the rendering is done only after all JS is run. The other reason has to do with performance. Rendering takes time!
$('#status').text('hi')
longCB();
You are not wrong when you say that the text is set to 'hi' before even calling longCB(), but since the thread is still running JS, the page won't get redrawn just yet. Using setTimeout simply adds the method call on a stack of things to do at some point in the future, and the browser is guaranteed to render between the end of a synchronous script and handling events or timeouts.

Related

How to make separate force layout render loop?

I'm using d3.js 3.5.6. How do we tick the force layout in our own render loop?
It seems that when I call force.start(), that automatically starts the force layout's own internal render loop (using requestAnimationFrame).
How do I prevent d3 from making a render loop, so that I can make my own render and call force.tick() myself?
This answer is plain wrong. Don't refer to it, don't use it.
I wrote a new one explaining how to do this correctly. I remember spending days digging into this as I though I had discovered an error. And, judging by the comments and the upvotes, I have managed to trick others—even including legends like Lars Kotthoff—to follow me down this wrong road. Anyways, I have learned a lot from my mistake. You only have to be ashamed of your errors if you do not take the chance to learn from them.
As soon as this answer is unaccepted I am going to delete it.
At first I was annoyed by the lack of code in the question and considered the answer to be rather easy and obvious. But, as it turned out, the problem has some unexpected implications and yields some interesting insights. If you are not interested in the details, you might want to have a look at my Final thoughts at the bottom for an executable solution.
I had seen code and documentation for doing the calculations of the force layout by explicitly calling force.tick.
# force.tick()
Runs the force layout simulation one step. This method can be used in conjunction with start and stop to compute a static layout. For example:
force.start();
for (var i = 0; i < n; ++i) force.tick();
force.stop();
This code always seemed dubious to me, but I took it for granted because the documentation had it and Mike Bostock himself made a "Static Force Layout" Block using the code from the docs. As it turns out, my intuition was right and both the Block as well as the documentation are wrong or at least widely off the track:
Calling start will do a lot of initialization of your nodes and links data (see documentation of nodes() and links(). You cannot just dismiss the call as you have experienced yourself. The force layout won't run without it.
Another thing start will eventually do is to fire up the processing loop by calling requestAnimationFrame or setTimeout, whatever is available, and provide force.tick as the callback. This results in an asynchronous processing which will repeatedly call force.tick, whereby doing the calculations and calling your tick handler if provided. The only non-hacky way to break this loop is to set alpha to below the hard-coded freezing point of 0.005 by calling force.alpha(0.005) or force.stop(). This will stop the loop on the next call to tick. Unless the timer is stopped this way, it will continue looping log0.99 (0.005 / 0.1) ≈ 298 times until alpha has dropped below the freezing point.
One should note, that this is not the case for the documentation or the Block. Hence, the tick-loop started by force.start() will continue running asynchronously and do its calculations.
The subsequent for-loop might or might not have any effect on the result of the force layout. If the timer happens to be still running in the background, this means concurrent calls to force.tick from the timer as well as from the for-loop. In any case will the calculations be stopped once alpha has dropped low enough when reaching a total of 298 calls to tick. This can be seen from the following lines:
force.tick = function() {
// simulated annealing, basically
if ((alpha *= 0.99) < 0.005) {
timer = null;
event.end({type: "end", alpha: alpha = 0});
return true;
}
// ...
}
From that point on you can call tick as often as you like without any change to the layout's outcome. The method is entered, but, because of the low value of alpha, will return immediately. All you will see is a repeated firing of end events.
To affect the number of iterations you have to control alpha.
The fact that the layout in the Block seems static is due to the fact that no callback for the "tick" event is registered which could update the SVG on every tick. The final result is only drawn once. And this result is ready after just 298 iterations, it won't be changed by subsequent, explicit calls to tick. The final call to force.stop() won't change anything either, it just sets alpha to 0. This does not have any effect on the result because the force layout has long come to an implicit halt.
Conclusion
Item 1. could be circumvented by a clever combination of starting and stopping the layout as in Stephen A. Thomas's great series "Understanding D3.js Force Layout" where from example 3 on he uses button controls to step through the calculations. This, however, will also come to a halt after 298 steps. To take full control of the iterations you need to
Provide a tick handler and immediately stop the timer by calling force.stop() therein. All calculations of this step will have been completed by then.
In your own loop calculate the new value for alpha. Setting this value by force.alpha() will restart the layout. Once the calculations of this next step are done, the tick handler will be executed resulting in an immediate stop as seen above. For this to work you will have to keep track of your alpha within your loop.
Final thoughts
The least invasive solution might be to call force.start() as normal and instead alter the force.tick function to immediately halt the timer. Since the timer in use is a normal d3.timer it may be interrupted by returning true from the callback, i.e. from the tick method. This could be achieved by putting a lightweight wrapper around the method. The wrapper will delegate to the original tick method, which is closed over, and will return true immediately afterwards, whereby stopping the timer.
force.tick = (function(forceTick) {
return function() { // This will be the wrapper around tick which returns true.
forceTick(); // Delegate to the original tick method.
return true; // Truth hurts. This will end the timer.
}
}(force.tick)); // Pass in the original method to be closed over.
As mentioned above you are now on your own managing the decreasing value of alpha to control the slowing of your layout's movements. This, however, will only require simple calculus and a loop to set alpha and call force.tick as you like. There are many ways this could be done; for a simple showcase I chose a rather verbose approach:
// To run the computing steps in our own loop we need
// to manage the cooling by ourselves.
var alphaStart = 0.1;
var alphaEnd = 0.005;
var alpha = alphaStart;
var steps = n * n;
var cooling = Math.pow(alphaEnd / alphaStart, 1 / steps);
// Calling start will initialize our layout and start the timer
// doing the actual calculations. This timer will halt, however,
// on the first call to .tick.
force.start();
// The loop will execute tick() a fixed number of times.
// Throughout the loop the cooling of the system is controlled
// by decreasing alpha to reach the freezing point once
// the desired number of steps is performed.
for (var i = 0; i < steps; i++) {
force.alpha(alpha*=cooling).tick();
}
force.stop();
To wrap this up, I forked Mike Bostock's Block to build an executable example myself.
You want a Static Force Layout as demonstrated by Mike Bostock in his Block. The documentation on force.tick() has the details:
# force.tick()
Runs the force layout simulation one step. This method can be used in conjunction with start and stop to compute a static layout. For example:
force.start();
for (var i = 0; i < n; ++i) force.tick();
force.stop();
As you have experienced yourself you cannot just dismiss the call to force.start() . Calling .start() will do a lot of initialization of your nodes and links data (see documentation of nodes() and links()). The force layout won't run without it. However, this will not start the force right away. Instead, it will schedule the timer to repeatedly call the .tick() method for asynchronous execution. It is important to notice that the first execution of the tick handler will not take place before all your current code has finished. For that reason, you can safely create your own render loop by calling force.tick().
For anyone interested in the gory details of why the scheduled timer won't run before the current code has finished I suggest thoroughly reading through:
DVK's answer (not the accepted one) to "Why is setTimeout(fn, 0) sometimes useful?".
John Reisig's excellent article on How JavaScript Timers Work.

Whack-A-Mole game with huge bug! Can I get some help fixing it?

I am writing a Whack-A-Mole game for class using HTML5, CSS3 and JavaScript. I have run into a very interesting bug where, at seemingly random intervals, my moles with stop changing their "onBoard" variables and, as a result, will stop being assigned to the board. Something similar has also happened with the holes, but not as often in my testing. All of this is completely independent of user interaction.
You guys and gals are my absolute last hope before I scrap the project and start completely from scratch. This has frustrated me to no end. Here is the Codepen and my github if you prefer to have the images.
Since Codepen links apparently require accompanying code, here is the function where I believe the problem is occuring.
// Run the game
function run() {
var interval = (Math.floor(Math.random() * 7) * 1000);
if(firstRound) {
renderHole(mole(), hole(), lifeSpan());
firstRound = false;
}
setTimeout(function() {
renderHole(mole(), hole(), lifeSpan());
run();
}, interval);
}
What I believe is happening is this. The function runs at random intervals, between 0-6 seconds. If the function runs too quickly, the data that is passed to my renderHole() function gets overwritten with the new data, thus causing the previous hole and mole to never be taken off the board (variable wise at least).
EDIT: It turns out that my issue came from my not having returns on my recursive function calls. Having come from a different language, I was not aware that, in JavaScript, functions return "undefined" if nothing else is indicated. I am, however, marking GameAlchemist's answer as the correct one due to the fact that my original code was convoluted and confusing, as well as redundant in places. Thank you all for your help!
You have done here and there in your code some design mistakes that, one after another, makes the code hard to read and follow, and quite impossible to debug.
the mole() function might return a mole... or not... or create a timeout to call itself later.. what will be done with the result when mole calls itself again ? nothing, so it will just be marked as onBoard never to be seen again.
--->>> Have a clear definition and a single responsibility for mole(): for instance 'returns an available non-displayed mole character or null'. And that's all, no count, no marking of the objects, just KISS (Keep It Simple S...) : it should always return a value and never trigger a timeout.
Quite the same goes for hole() : return a free hole or null, no marking, no timeout set.
render should be simplified : get a mole, get a hole, if either couldn't be found bye bye. If a mole+hole was found, just setup the new mole/hole couple + event handler (in a separate function). Your main run function will ensure to try again and again to spawn moles.

Why is ReactJS not performant like raw JS for simple, stupid proof of concept?

Years ago, I heard about a nice 404 page and implemented a copy.
In working with ReactJS, the same idea is intended to be implemented, but it is slow and jerky in its motion, and after a while Chrome gives it an "unresponsive script" warning, pinpointed to line 1226, "var position = index % repeated_tokens.length;", with a few hundred milliseconds' delay between successive calls. The script consistently goes beyond an unresponsive page to bringing a computer to its knees.
Obviously, they're not the same implementation, although the ReactJS version is derived from the "I am not using jQuery yet" version. But beyond that, why is it bogging? Am I making a deep stack of closures? Why is the ReactJS port slower than the bare JavaScript original?
In both cases the work is driven by minor arithmetic and there is nothing particularly interesting about the code or what it is doing.
--UPDATE--
I see I've gotten a downvote and three close votes...
This appears to have gotten response from people who are (a) saying something sensible and (b) contradicting what Pete Hunt and other people have said.
What is claimed, among other things, by Hunt and Facebook's ReactJS video, is that the synthetic DOM is lightning-fast, enough to pull 60 frames per second on a non-JIT iPhone. And they've left an optimization hook to say "Ignore this portion of the DOM in your fast comparison," which I've used elsewhere to disclaim jurisdiction of a non-ReactJS widget.
#EdBallot's suggestion that it's "an extreme (and unnecessary) amount of work to create and render an element, and do a single document.getElementById. Now I'm factoring out that last bit; DOM manipulation is slow. But the responses here are hard to reconcile with what Facebook has been saying about performant ReactJS. There is a "Crunch all you want; we'll make more" attitude about (theoretically) throwing away the DOM and making a new one, which is lightning-fast because it's done in memory without talking to the real DOM.
In many cases I want something more surgical and can attempt to change the smallest area possible, but the letter and spirit of ReactJS videos I've seen is squarely in the spirit of "Crunch all you want; we'll make more."
Off to trying suggested edits to see what they will do...
I didn't look at all the code, but for starters, this is rather inefficient
var update = function() {
React.render(React.createElement(Pragmatometer, null),
document.getElementById('main'));
for(var instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
save('Scratchpad', document.getElementById('scratchpad').value);
};
var update_interval = setInterval(update, 100);
It is doing an extreme (and unnecessary) amount of work and it is being done every 100ms. Among other things, it is calling:
React.createElement
React.render
document.getElementById
Probably with the amount of JS objects being created and released, your update function plus garbage collection is taking longer than 100ms, effectively taking the computer to its knees and lower.
At the very least, I'd recommend caching as much as you can outside of the interval callback. Also no need to call React.render multiple times. Once it is rendered into the dom, use setProps or forceUpdate to cause it to render changes.
Here's an example of what I mean:
var mainComponent = React.createElement(Pragmatometer, null);
React.render(mainComponent,
document.getElementById('main'));
var update = function() {
mainComponent.forceUpdate();
for(var instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
save('Scratchpad', document.getElementById('scratchpad').value);
};
var update_interval = setInterval(update, 100);
Beyond that, I'd also recommend moving the setInterval code into whatever React component is rendering that stuff (the Scratchpad component?).
A final comment: one of the downsides of using setInterval is that it doesn't wait for the callback function to complete before queuing up the next callback. An alternative is to use setTimeout with the callback setting up the next setTimeout, like this
var update = function() {
// do some stuff
// update is done to setup the next timeout
setTimeout(update, 100);
};
setTimeout(update, 100);

CreateJS: Unable to get tweening to work

So,
I'm just starting to learn CreateJS and I encountered my first problem: I can't get tweening to work (as I expect it should work).
Here is the example: http://www.hakoniemi.net/labs/createjs-test/
I want to get that cloud to move from right to left - at the moment it only jumps to the target.
The code looks:
createjs.Tween.get(stack["cloud"]).to({"x":25}, 1000).call(test);
where createjs.Tween.get(stack["cloud"]) is valid and function test is executed. However there's no visual effect of 1000ms taking place at all.
I've looked through the tutorials and this is how things should work, but they're not. What am I doing wrong?
Edit: if I re-execute code in console with different value, then tweening and visual effect happens normally (here's a version where setTimeout is used: http://www.hakoniemi.net/labs/createjs-test/index2.html)
You have a type problem when setting the initial x value in
if (this.getAttribute("x")) {
ref.x = this.getAttribute("x");
}
The problem is that getAttribute() returns a string, which you can verify outputing Object.prototype.toString.call(ref.x). This way, it seems the first time the tween tries to run it can't do the proper math. In the end, it correctly updates the value to the end value as a number and that's why next calls to the same method work properly.
You can fix this just by making sure that ref.x is a number. For example:
if (this.getAttribute("x")) {
ref.x = parseInt(this.getAttribute("x"));
}
You can see it working in this fiddle.
One last thing, BitmapImageLoaded is adding the assets to the stage as soon as they are loaded. If your clouds image gets loaded before the background, it will be placed under it and you won't be able to see them. (just in case :))

Why is setTimeout(fn, 0) sometimes useful?

I've recently run into a rather nasty bug, wherein the code was loading a <select> dynamically via JavaScript. This dynamically loaded <select> had a pre-selected value. In IE6, we already had code to fix the selected <option>, because sometimes the <select>'s selectedIndex value would be out of sync with the selected <option>'s index attribute, as below:
field.selectedIndex = element.index;
However, this code wasn't working. Even though the field's selectedIndex was being set correctly, the wrong index would end up being selected. However, if I stuck an alert() statement in at the right time, the correct option would be selected. Thinking this might be some sort of timing issue, I tried something random that I'd seen in code before:
var wrapFn = (function() {
var myField = field;
var myElement = element;
return function() {
myField.selectedIndex = myElement.index;
}
})();
setTimeout(wrapFn, 0);
And this worked!
I've got a solution for my problem, but I'm uneasy that I don't know exactly why this fixes my problem. Does anyone have an official explanation? What browser issue am I avoiding by calling my function "later" using setTimeout()?
In the question, there existed a race condition between:
The browser's attempt to initialize the drop-down list, ready to have its selected index updated, and
Your code to set the selected index
Your code was consistently winning this race and attempting to set drop-down selection before the browser was ready, meaning that the bug would appear.
This race existed because JavaScript has a single thread of execution that is shared with page rendering. In effect, running JavaScript blocks the updating of the DOM.
Your workaround was:
setTimeout(callback, 0)
Invoking setTimeout with a callback, and zero as the second argument will schedule the callback to be run asynchronously, after the shortest possible delay - which will be around 10ms when the tab has focus and the JavaScript thread of execution is not busy.
The OP's solution, therefore was to delay by about 10ms, the setting of the selected index. This gave the browser an opportunity to initialize the DOM, fixing the bug.
Every version of Internet Explorer exhibited quirky behaviors and this kind of workaround was necessary at times. Alternatively it might have been a genuine bug in the OP's codebase.
See Philip Roberts talk "What the heck is the event loop?" for more thorough explanation.
Preface:
Some of the other answers are correct but don't actually illustrate what the problem being solved is, so I created this answer to present that detailed illustration.
As such, I am posting a detailed walk-through of what the browser does and how using setTimeout() helps. It looks longish but is actually very simple and straightforward - I just made it very detailed.
UPDATE: I have made a JSFiddle to live-demonstrate the explanation below: http://jsfiddle.net/C2YBE/31/ . Many thanks to #ThangChung for helping to kickstart it.
UPDATE2: Just in case JSFiddle web site dies, or deletes the code, I added the code to this answer at the very end.
DETAILS:
Imagine a web app with a "do something" button and a result div.
The onClick handler for "do something" button calls a function "LongCalc()", which does 2 things:
Makes a very long calculation (say takes 3 min)
Prints the results of calculation into the result div.
Now, your users start testing this, click "do something" button, and the page sits there doing seemingly nothing for 3 minutes, they get restless, click the button again, wait 1 min, nothing happens, click button again...
The problem is obvious - you want a "Status" DIV, which shows what's going on. Let's see how that works.
So you add a "Status" DIV (initially empty), and modify the onclick handler (function LongCalc()) to do 4 things:
Populate the status "Calculating... may take ~3 minutes" into status DIV
Makes a very long calculation (say takes 3 min)
Prints the results of calculation into the result div.
Populate the status "Calculation done" into status DIV
And, you happily give the app to users to re-test.
They come back to you looking very angry. And explain that when they clicked the button, the Status DIV never got updated with "Calculating..." status!!!
You scratch your head, ask around on StackOverflow (or read docs or google), and realize the problem:
The browser places all its "TODO" tasks (both UI tasks and JavaScript commands) resulting from events into a single queue. And unfortunately, re-drawing the "Status" DIV with the new "Calculating..." value is a separate TODO which goes to the end of the queue!
Here's a breakdown of the events during your user's test, contents of the queue after each event:
Queue: [Empty]
Event: Click the button. Queue after event: [Execute OnClick handler(lines 1-4)]
Event: Execute first line in OnClick handler (e.g. change Status DIV value). Queue after event: [Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]. Please note that while the DOM changes happen instantaneously, to re-draw the corresponding DOM element you need a new event, triggered by the DOM change, that went at the end of the queue.
PROBLEM!!! PROBLEM!!! Details explained below.
Event: Execute second line in handler (calculation). Queue after: [Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value].
Event: Execute 3rd line in handler (populate result DIV). Queue after: [Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result].
Event: Execute 4th line in handler (populate status DIV with "DONE"). Queue: [Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value].
Event: execute implied return from onclick handler sub. We take the "Execute OnClick handler" off the queue and start executing next item on the queue.
NOTE: Since we already finished the calculation, 3 minutes already passed for the user. The re-draw event didn't happen yet!!!
Event: re-draw Status DIV with "Calculating" value. We do the re-draw and take that off the queue.
Event: re-draw Result DIV with result value. We do the re-draw and take that off the queue.
Event: re-draw Status DIV with "Done" value. We do the re-draw and take that off the queue.
Sharp-eyed viewers might even notice "Status DIV with "Calculating" value flashing for fraction of a microsecond - AFTER THE CALCULATION FINISHED
So, the underlying problem is that the re-draw event for "Status" DIV is placed on the queue at the end, AFTER the "execute line 2" event which takes 3 minutes, so the actual re-draw doesn't happen until AFTER the calculation is done.
To the rescue comes the setTimeout(). How does it help? Because by calling long-executing code via setTimeout, you actually create 2 events: setTimeout execution itself, and (due to 0 timeout), separate queue entry for the code being executed.
So, to fix your problem, you modify your onClick handler to be TWO statements (in a new function or just a block within onClick):
Populate the status "Calculating... may take ~3 minutes" into status DIV
Execute setTimeout() with 0 timeout and a call to LongCalc() function.
LongCalc() function is almost the same as last time but obviously doesn't have "Calculating..." status DIV update as first step; and instead starts the calculation right away.
So, what does the event sequence and the queue look like now?
Queue: [Empty]
Event: Click the button. Queue after event: [Execute OnClick handler(status update, setTimeout() call)]
Event: Execute first line in OnClick handler (e.g. change Status DIV value). Queue after event: [Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value].
Event: Execute second line in handler (setTimeout call). Queue after: [re-draw Status DIV with "Calculating" value]. The queue has nothing new in it for 0 more seconds.
Event: Alarm from the timeout goes off, 0 seconds later. Queue after: [re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)].
Event: re-draw Status DIV with "Calculating" value. Queue after: [execute LongCalc (lines 1-3)]. Please note that this re-draw event might actually happen BEFORE the alarm goes off, which works just as well.
...
Hooray! The Status DIV just got updated to "Calculating..." before the calculation started!!!
Below is the sample code from the JSFiddle illustrating these examples: http://jsfiddle.net/C2YBE/31/ :
HTML code:
<table border=1>
<tr><td><button id='do'>Do long calc - bad status!</button></td>
<td><div id='status'>Not Calculating yet.</div></td>
</tr>
<tr><td><button id='do_ok'>Do long calc - good status!</button></td>
<td><div id='status_ok'>Not Calculating yet.</div></td>
</tr>
</table>
JavaScript code: (Executed on onDomReady and may require jQuery 1.9)
function long_running(status_div) {
var result = 0;
// Use 1000/700/300 limits in Chrome,
// 300/100/100 in IE8,
// 1000/500/200 in FireFox
// I have no idea why identical runtimes fail on diff browsers.
for (var i = 0; i < 1000; i++) {
for (var j = 0; j < 700; j++) {
for (var k = 0; k < 300; k++) {
result = result + i + j + k;
}
}
}
$(status_div).text('calculation done');
}
// Assign events to buttons
$('#do').on('click', function () {
$('#status').text('calculating....');
long_running('#status');
});
$('#do_ok').on('click', function () {
$('#status_ok').text('calculating....');
// This works on IE8. Works in Chrome
// Does NOT work in FireFox 25 with timeout =0 or =1
// DOES work in FF if you change timeout from 0 to 500
window.setTimeout(function (){ long_running('#status_ok') }, 0);
});
Take a look at John Resig's article about How JavaScript Timers Work. When you set a timeout, it actually queues the asynchronous code until the engine executes the current call stack.
setTimeout() buys you some time until the DOM elements are loaded, even if is set to 0.
Check this out: setTimeout
There are conflicting upvoted answers here, and without proof there is no way to know whom to believe. Here is proof that #DVK is right and #SalvadorDali is incorrect. The latter claims:
"And here is why: it is not possible to have setTimeout with a time
delay of 0 milliseconds. The Minimum value is determined by the
browser and it is not 0 milliseconds. Historically browsers sets this
minimum to 10 milliseconds, but the HTML5 specs and modern browsers
have it set at 4 milliseconds."
The 4ms minimum timeout is irrelevant to what is happening. What really happens is that setTimeout pushes the callback function to the end of the execution queue. If after setTimeout(callback, 0) you have blocking code which takes several seconds to run, the callback will not be executed for several seconds, until the blocking code has finished. Try this code:
function testSettimeout0 () {
var startTime = new Date().getTime()
console.log('setting timeout 0 callback at ' +sinceStart())
setTimeout(function(){
console.log('in timeout callback at ' +sinceStart())
}, 0)
console.log('starting blocking loop at ' +sinceStart())
while (sinceStart() < 3000) {
continue
}
console.log('blocking loop ended at ' +sinceStart())
return // functions below
function sinceStart () {
return new Date().getTime() - startTime
} // sinceStart
} // testSettimeout0
Output is:
setting timeout 0 callback at 0
starting blocking loop at 5
blocking loop ended at 3000
in timeout callback at 3033
Browsers have a process called "main thread", that is responsible for executing some JavaScript tasks, UI updates e.g.: painting, redraw, reflow, etc.
JavaScript tasks are queued to a message queue and then are dispatched to the browser's main thread to be executed.
When UI updates are generated while the main thread is busy, tasks are added into the message queue.
Both of these two top-rated answers are wrong. Check out the MDN description on the concurrency model and the event loop, and it should become clear what's going on (that MDN resource is a real gem). And simply using setTimeout can be adding unexpected problems in your code in addition to "solving" this little problem.
What's actually going on here is not that "the browser might not be quite ready yet because concurrency," or something based on "each line is an event that gets added to the back of the queue".
The jsfiddle provided by DVK indeed illustrates a problem, but his explanation for it isn't correct.
What's happening in his code is that he's first attaching an event handler to the click event on the #do button.
Then, when you actually click the button, a message is created referencing the event handler function, which gets added to the message queue. When the event loop reaches this message, it creates a frame on the stack, with the function call to the click event handler in the jsfiddle.
And this is where it gets interesting. We're so used to thinking of Javascript as being asynchronous that we're prone to overlook this tiny fact: Any frame has to be executed, in full, before the next frame can be executed. No concurrency, people.
What does this mean? It means that whenever a function is invoked from the message queue, it blocks the queue until the stack it generates has been emptied. Or, in more general terms, it blocks until the function has returned. And it blocks everything, including DOM rendering operations, scrolling, and whatnot. If you want confirmation, just try to increase the duration of the long running operation in the fiddle (e.g. run the outer loop 10 more times), and you'll notice that while it runs, you cannot scroll the page. If it runs long enough, your browser will ask you if you want to kill the process, because it's making the page unresponsive. The frame is being executed, and the event loop and message queue are stuck until it finishes.
So why this side-effect of the text not updating? Because while you have changed the value of the element in the DOM — you can console.log() its value immediately after changing it and see that it has been changed (which shows why DVK's explanation isn't correct) — the browser is waiting for the stack to deplete (the on handler function to return) and thus the message to finish, so that it can eventually get around to executing the message that has been added by the runtime as a reaction to our mutation operation, and in order to reflect that mutation in the UI.
This is because we are actually waiting for code to finish running. We haven't said "someone fetch this and then call this function with the results, thanks, and now I'm done so imma return, do whatever now," like we usually do with our event-based asynchronous Javascript. We enter a click event handler function, we update a DOM element, we call another function, the other function works for a long time and then returns, we then update the same DOM element, and then we return from the initial function, effectively emptying the stack. And then the browser can get to the next message in the queue, which might very well be a message generated by us by triggering some internal "on-DOM-mutation" type event.
The browser UI cannot (or chooses not to) update the UI until the currently executing frame has completed (the function has returned). Personally, I think this is rather by design than restriction.
Why does the setTimeout thing work then? It does so, because it effectively removes the call to the long-running function from its own frame, scheduling it to be executed later in the window context, so that it itself can return immediately and allow the message queue to process other messages. And the idea is that the UI "on update" message that has been triggered by us in Javascript when changing the text in the DOM is now ahead of the message queued for the long-running function, so that the UI update happens before we block for a long time.
Note that a) The long-running function still blocks everything when it runs, and b) you're not guaranteed that the UI update is actually ahead of it in the message queue. On my June 2018 Chrome browser, a value of 0 does not "fix" the problem the fiddle demonstrates — 10 does. I'm actually a bit stifled by this, because it seems logical to me that the UI update message should be queued up before it, since its trigger is executed before scheduling the long-running function to be run "later". But perhaps there're some optimisations in the V8 engine that may interfere, or maybe my understanding is just lacking.
Okay, so what's the problem with using setTimeout, and what's a better solution for this particular case?
First off, the problem with using setTimeout on any event handler like this, to try to alleviate another problem, is prone to mess with other code. Here's a real-life example from my work:
A colleague, in a mis-informed understanding on the event loop, tried to "thread" Javascript by having some template rendering code use setTimeout 0 for its rendering. He's no longer here to ask, but I can presume that perhaps he inserted timers to gauge the rendering speed (which would be the return immediacy of functions) and found that using this approach would make for blisteringly fast responses from that function.
First problem is obvious; you cannot thread javascript, so you win nothing here while you add obfuscation. Secondly, you have now effectively detached the rendering of a template from the stack of possible event listeners that might expect that very template to have been rendered, while it may very well not have been. The actual behaviour of that function was now non-deterministic, as was — unknowingly so — any function that would run it, or depend on it. You can make educated guesses, but you cannot properly code for its behaviour.
The "fix" when writing a new event handler that depended on its logic was to also use setTimeout 0. But, that's not a fix, it is hard to understand, and it is no fun to debug errors that are caused by code like this. Sometimes there's no problem ever, other times it concistently fails, and then again, sometimes it works and breaks sporadically, depending on the current performance of the platform and whatever else happens to going on at the time. This is why I personally would advise against using this hack (it is a hack, and we should all know that it is), unless you really know what you're doing and what the consequences are.
But what can we do instead? Well, as the referenced MDN article suggests, either split the work into multiple messages (if you can) so that other messages that are queued up may be interleaved with your work and executed while it runs, or use a web worker, which can run in tandem with your page and return results when done with its calculations.
Oh, and if you're thinking, "Well, couldn't I just put a callback in the long-running function to make it asynchronous?," then no. The callback doesn't make it asynchronous, it'll still have to run the long-running code before explicitly calling your callback.
If you don't want to watch a whole video, here's a simple explanation of the things one needs to understand, in order to be able to understand the answer to this question:
JavaScript is single-threaded meaning it does only one thing at a time when running.
But the environments in which the JavaScript is running, can be multi-threaded. E.g., browsers are often multi-threaded creatures, i.e., are able to do multiple things at a time. So they can run JavaScript and at the same time keep track of dealing with other stuff too.
From this point on, we're talking about JavaScript "in browsers". Things like setTimeout are indeed browser things, and are not part of the JavaScript itself.
The thing that allows JavaScript to run asynchronously is the multi-threaded browser! Other than the main space Javascript uses (called the the call stack) to put each line of code on and run them one by one, browsers also provide JavaScript with another space to put things on.
Now let's call that other space the second space.
Let's assume fn is a function. The important thing to understand here is that fn(); call is not equal to the setTimeout(fn, 0); call as will be explained further below.
Instead of a 0 delay, let's assume another delay first, e.g., 5000 milliseconds: setTimeout(fn, 5000);. It's important to note that this is still a "function call", so it has to be put on the main space, and removed from it when it's done, but wait!, we don't like a whole lengthy and boring 5 seconds delay. That would block the main space and will not allow JavaScript to run ANYTHING else in the meantime.
Thankfully this is not how the browser designers designed them to work. Instead, this call(setTimeout(fn, 5000);) is done instantly. This is very important: Even with the 5000 milliseconds delay, this function call is complete in an instant! What will happen next? It gets removed from the main space. Where will it be put on? (because we don't want to lose it). You might have guessed right: The browser hears this call and puts it on the second space.
The browser keeps track of the 5 seconds delay and once it's passed, it looks at the main space, and "WHEN IT'S EMPTY", puts the fn(); call back on it. That is how the setTimeout works.
So, back to the setTimeout(fn, 0), even though the delay is zero, this is still a call to the browser, and the browser hears it instantly and picks it up, and puts it on the second space and puts it back on the main space only when the main space is empty again, and not really 0 milliseconds later.
I really recommend watching that video as well since he's explained it really well, and opens technical things up more.
One reason to do that is to defer the execution of code to a separate, subsequent event loop. When responding to a browser event of some kind (mouse click, for example), sometimes it's necessary to perform operations only after the current event is processed. The setTimeout() facility is the simplest way to do it.
edit now that it's 2015 I should note that there's also requestAnimationFrame(), which isn't exactly the same but it's sufficiently close to setTimeout(fn, 0) that it's worth mentioning.
This is an old questions with old answers. I wanted to add a new look at this problem and to answer why is this happens and not why is this useful.
So you have two functions:
var f1 = function () {
setTimeout(function(){
console.log("f1", "First function call...");
}, 0);
};
var f2 = function () {
console.log("f2", "Second call...");
};
and then call them in the following order f1(); f2(); just to see that the second one executed first.
And here is why: it is not possible to have setTimeout with a time delay of 0 milliseconds. The Minimum value is determined by the browser and it is not 0 milliseconds. Historically browsers sets this minimum to 10 milliseconds, but the HTML5 specs and modern browsers have it set at 4 milliseconds.
If nesting level is greater than 5, and timeout is less than 4, then
increase timeout to 4.
Also from mozilla:
To implement a 0 ms timeout in a modern browser, you can use
window.postMessage() as described here.
P.S. information is taken after reading the following article.
Since it is being passed a duration of 0, I suppose it is in order to remove the code passed to the setTimeout from the flow of execution. So if it's a function that could take a while, it won't prevent the subsequent code from executing.
The other thing this does is push the function invocation to the bottom of the stack, preventing a stack overflow if you are recursively calling a function. This has the effect of a while loop but lets the JavaScript engine fire other asynchronous timers.
By calling setTimeout you give the page time to react to the whatever the user is doing. This is particularly helpful for functions run during page load.
Some other cases where setTimeout is useful:
You want to break a long-running loop or calculation into smaller components so that the browser doesn't appear to 'freeze' or say "Script on page is busy".
You want to disable a form submit button when clicked, but if you disable the button in the onClick handler the form will not be submitted. setTimeout with a time of zero does the trick, allowing the event to end, the form to begin submitting, then your button can be disabled.
The problem was you were trying to perform a Javascript operation on a non existing element. The element was yet to be loaded and setTimeout() gives more time for an element to load in the following ways:
setTimeout() causes the event to be ansynchronous therefore being executed after all the synchronous code, giving your element more time to load. Asynchronous callbacks like the callback in setTimeout() are placed in the event queue and put on the stack by the event loop after the stack of synchronous code is empty.
The value 0 for ms as a second argument in function setTimeout() is often slightly higher (4-10ms depending on browser). This slightly higher time needed for executing the setTimeout() callbacks is caused by the amount of 'ticks' (where a tick is pushing a callback on the stack if stack is empty) of the event loop. Because of performance and battery life reasons the amount of ticks in the event loop are restricted to a certain amount less than 1000 times per second.
The answers about execution loops and rendering the DOM before some other code completes are correct. Zero second timeouts in JavaScript help make the code pseudo-multithreaded, even though it is not.
I want to add that the BEST value for a cross browser / cross platform zero-second timeout in JavaScript is actually about 20 milliseconds instead of 0 (zero), because many mobile browsers can't register timeouts smaller than 20 milliseconds due to clock limitations on AMD chips.
Also, long-running processes that do not involve DOM manipulation should be sent to Web Workers now, as they provide true multithreaded execution of JavaScript.
setTimout on 0 is also very useful in the pattern of setting up a deferred promise, which you want to return right away:
myObject.prototype.myMethodDeferred = function() {
var deferredObject = $.Deferred();
var that = this; // Because setTimeout won't work right with this
setTimeout(function() {
return myMethodActualWork.call(that, deferredObject);
}, 0);
return deferredObject.promise();
}
//When need "new a", setTimeout(fn, 0) is useful, when need to wait some action. Example:
var a = function (){console.log('a');};
var b = function(){setTimeout(b, 100);}; //wait some action before override this function
//without setTimeout:
console.log('no setTimeout: b.toString():', b.toString());
b(); //"b" is an old function
console.log('no setTieout: a.toString(): ', a.toString());
a(); //and "a" is not overrided
setTimeout(//but with setTimeout(fn, 0):
function(){
console.log('After timeout 0, b.toString(): ', b.toString());
b(); //"b" is a new function
console.log('After timeout 0, a.toString(): ', a.toString());
a(); //and "a" is overrided
},
0
);
//override var "b", which was been undefined
b = function (){
a = function(){console.log('new a');};
}
Javascript is single threaded application so that don't allow to run function concurrently so to achieve this event loops are use. So exactly what setTimeout(fn, 0) do that its pussed into task quest which is executed when your call stack is empty. I know this explanation is pretty boring, so i recommend you to go through this video this will help you how things work under the hood in browser.
Check out this video:- https://www.youtube.com/watch?time_continue=392&v=8aGhZQkoFbQ

Categories