javascript tutorial - [Solved-5 Solutions] Why is setTimeout(fn,0) sometimes useful? - javascript - java script - javascript array


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;
click below button to copy the code. By JavaScript tutorial team

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 WE stuck an alert() statement in at the right time, the correct option would be selected. Thinking this might be some sort of timing issue, We 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);
click below button to copy the code. By JavaScript tutorial team

And this worked! I've got a solution for my problem, but I'm uneasy that WE don't know exactly why this fixes my problem. Does anyone have an official explanation? What browser issue am WE avoiding by calling my function "later" using setTimeout()?

Solution 1:

This works because you're doing co-operative multi-tasking. A browser has to do a number of things pretty much all at once, and just one of those is execute JavaScript. But one of the things JavaScript is very often used for is to ask the browser to build a display element. This is often assumed to be done synchronously (particularly as JavaScript is not executed in parallel) but there is no guarantee this is the case and JavaScript does not have a well-defined mechanism for waiting.

The solution is to "pause" the JavaScript execution to let the rendering threads catch up. And this is the effect that setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually gives the browser a chance to finish doing some non-JavaScript things that have been waiting to finish before attending to this new piece of JavaScript. (In actuality, setTimeout() re-queues the new JavaScript at the end of the execution queue. See the comments for links to a longer explanation.) IE6 just happens to be more prone to this error, but WE have seen it occur on older versions of Mozilla and in Firefox.

Solution 2:

Take a look at John Resig's article about How JavaScript Timers Work. When we set a timeout, it actually queues the asynchronous code until the engine executes the current call stack.

Solution 3:

Most browsers have a process called main thread, that is responsible for execute some JavaScript tasks, UWE updates e.g.: painting, redraw or reflow, etc.

Some JavaScript execution and UWE update tasks are queued to the browser message queue, then are dispatched to the browser main thread to be executed.

When UWE updates are generated while the main thread is busy, the tasks are added into the message queue.

setTimeout(fn, 0); add this fn to the end of the queue to be executed. It schedules a task to be added on the message queue after a given amount of time.

Solution 4:

setTimeout() buys we some time until the DOM elements are loaded, even if is set to 0. Check this out: setTimeout

Solution 5:

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 WE 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.

Related Searches to javascript tutorial - Why is setTimeout(fn,0) sometimes useful?