Coder Perfect

Why is setTimeout(fn, 0) beneficial at times?

Problem

I recently came across a severe problem in which the code was dynamically loading a select> using JavaScript. A value was pre-selected in this dynamically loaded select>. We already had code in IE6 to repair the selected option> because the selectedIndex value of the select> was occasionally out of sync with the index attribute of the selected option>, as shown below:

field.selectedIndex = element.index;

This code, however, did not work. Even if the field’s selectedIndex was correctly set, the incorrect index would be selected. The proper option would be selected if I used an alert() statement at the appropriate moment. I tried something odd that I’d seen in code before, thinking it might be a timing issue:

var wrapFn = (function() {
    var myField = field;
    var myElement = element;

    return function() {
        myField.selectedIndex = myElement.index;
    }
})();
setTimeout(wrapFn, 0);

And this worked!

I’ve found a solution to my problem, but I’m concerned that I don’t understand why it works. Is there a formal explanation available? What browser issue am I avoiding by using setTimeout() to call my function “later”?

Asked by Dan Lew

Solution #1

There was a race condition in the question between:

Your code was continually winning this race, attempting to establish drop-down selections before the browser was ready, resulting in the issue.

Because JavaScript uses a single thread of execution that is shared with page rendering, there was a race. Running JavaScript effectively prevents the DOM from being updated.

Your workaround was:

setTimeout(callback, 0)

When the tab has focus and the JavaScript thread of execution is not busy, invoking setTimeout with a callback and zero as the second argument will schedule the callback to run asynchronously, after the least possible delay – which will be roughly 10ms.

As a result, the OP’s solution was to postpone the setting of the specified index by around 10 milliseconds. This allowed the browser to fix the error by allowing it to initialize the DOM.

Every version of Internet Explorer had odd quirks, and this type of workaround was sometimes essential. It could have also been a genuine bug in the OP’s coding.

For a more detailed explanation, see Philip Roberts’ lecture “What the Heck is the Event Loop?”

Answered by staticsan

Solution #2

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 a result, I’m going to walk you through what the browser does and how setTimeout() can help. It appears to be lengthy, but it is actually rather simple and straightforward; I just went overboard with the details.

UPDATE: I’ve created a JSFiddle to demonstrate the reasoning below in real time: http://jsfiddle.net/C2YBE/31/ Many thanks to @ThangChung for assisting with the launch.

UPDATE2: In case the JSFiddle website goes down or the code is removed, I’ve included the code at the bottom of my answer.

DETAILS:

Consider a web application that includes a “do something” button and a result div.

The “do something” button’s onClick handler launches a procedure called “LongCalc(),” which accomplishes two things:

Now, your users begin to test this by clicking the “do anything” button, and the page sits there doing nothing for three minutes. They become irritated, click the button again, wait one minute, nothing occurs, click the button again…

The issue is self-evident: you need a “Status” DIV to show what’s going on. Let’s have a look at how that goes.

So you create a “Status” DIV (which was previously empty) and change the onclick handler (method LongCalc()) to do the following:

And you’re happy to hand out the software to users for re-testing.

They return to you appearing enraged. Also, explain why the State DIV was never updated with the “Calculating…” status after they clicked the button!!!

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!

The following is a list of the events that occurred during your user’s test, as well as the contents of the queue after each event:

So, the fundamental issue is that the “Status” DIV re-draw event is queued at the end, AFTER the 3 minute “execute line 2” event, so the actual re-draw doesn’t happen until AFTER the computation is completed.

setTimeout comes to the rescue (). How does it assist? Because when you call long-running code through setTimeout, you actually produce two events: the setTimeout execution and a separate queue entry for the code being performed (due to the 0 timeout).

So, to solve your problem, you change your onClick handler to include TWO statements (either in a new function or as a block within onClick):

So, how does the event sequence and queue currently look?

Hooray! Before the calculation began, the Status DIV was modified to “Calculating…”!!!

The following is JSFiddle code that illustrates 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>

Code in JavaScript: (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);
});

Answered by DVK

Solution #3

Take a peek at John Resig’s How JavaScript Timers Work post. When you set a timeout, it actually queues the asynchronous code until the engine executes the current call stack.

Answered by Andy

Solution #4

Even if set to 0, setTimeout() buys you some time until the DOM elements are loaded.

Take a look at this: setTimeout

Answered by Jose Basilio

Solution #5

able to perform some JavaScript activities, such as painting, redrawing, reflowing, and so forth. JavaScript jobs are queued in a message queue before being transmitted to the browser’s main thread. Tasks are added to the message queue when UI updates are generated while the main thread is busy.

Answered by 14 revs

Post is based on https://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful