Why is it sometimes necessary to setTimeout with a value of 0 (zero)?

Asked

Viewed 3,474 times

20

I have observed for some time that some types of event only work correctly when we define a certain function that is called with the setTimeout worthwhile 0. On the contrary, if we call this function without the setTimeout, unwanted effects occur.

I will illustrate with an input that receives a value and is transformed to lowercase.

$(function() {
    $('#teste-1').keydown(function() {
        $(this).val(this.value.toLowerCase());
    })

    $('#teste-2').keydown(function() {
        var that = this;

        setTimeout(function() {
            $(that).val(that.value.toLowerCase());

        }, 0);
    })
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input id="teste-1" />
<input id="teste-2" />

Notice that when we type a text in the first input, the last character is always uppercase. In the second case, with setTimeout 0, all characters are converted to lowercase correctly.

I’d like to know why exactly this happens!

Why does javascript need setTimeout to behave in the expected way - the expected way is the way it works when using setTimeout.

  • 1

    Before anyone comes to speak in my ear: Related English: http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful

  • I can’t read as well in English and the Google Translation becomes horrible!

  • 2

    Excellent question @Wallacemaxters, also wanted to know this. + 1

3 answers

20


This is usually used for mistakes or long loops within the callback do not affect what comes after the setTimeout, is an attempt to "simulate" the Multiple threads (multithreaded), is not a thread true, but works on the same line.

With the 0 (zero), it will run itself at the same time as setTimeout is called, but it will not be necessary what comes after the setTimeout wait for the process to end, it is as if the callback was executed in a separate "process"

For example:

function meuCallback() {
    console.log("Level2");
}

console.log("Level1");
setTimeout(meuCallback, 0);
console.log("Level1");

In the example it will deliver something like:

Level1
Level2
Level1

But it is good to note that each engine (browser technology Ecmascript - popularly called Javascript) adjusts as needed and tries its way to get better performance, sometimes the same script can deliver something like:

Level1
Level1
Level2

An example that some people use to avoid mistakes would be something like:

function meuCallbackComError() {
    (a + b);
}

console.log("Execução 1: Level1");
setTimeout(meuCallbackComError, 0);
console.log("Execução 2: Level1");

See that in the log appears something like:

Execution 1: Level1
Execution 2: Level1
Uncaught Referenceerror: a is not defined

That is to say the second console.log was not affected by the error.

Why not use zero in setTimeout

It does not mean that you will never use, for example just to avoid mistakes that can be caused inside the callback the zero will be enough, the situations that we should avoid the 0 is when processes of the browser itself occur that may take time, such as rendering images after the onload, we often use the setTimeout to wait for another process to end, for example an image inserted by javascript, even using Image.onload not yet rendered within a millionth of a second (something imperceptible to humans), so a small "delay" can help, such as:

  • setTimeout(..., 1) - works for most rendering cases this can work
  • setTimeout(..., 10) - this may be preferable to others and hardly a human will realize
  • setTimeout(..., 100) - in some cases we need a longer delay, where there is an element that will take time to render (hardly a human will see this).

Alternatives to setTimeout

It works well in most cases, but it should be noted that scripts that take time to execute will still freeze webbrowser for some time (varying according to the script), even using the setTimeout.

I recommend you read this other answer, it explains the callbacks and the setTimeout:

There are currently more functional solutions to prevent freezing, for example:

However if you just want to prevent small delays or errors that might occur, then just use the setTimeout, if you want to run scripts that take more than 500ms to process then the Web Workers or the AMD may be useful to you.

Note that the AMD is being used by several libraries, such as the jQuery for example. See a snippet of the code jQuery:

if ( typeof define === "function" && define.amd ) {
    define( "jquery", [], function() {
        return jQuery;
    });
}
  • 1

    William, incredible as it seems, was having problems in a plugin that serves to detect that all images inside an element are loaded. I had to change the function internally to setTimeout with zero, because the size of the images were not being captured, when there was no setTimeout. This has to do with loading the browser as well?

  • @Wallacemaxters yes exact, in this case I usually use setTimeout(, 10);, rarely use with 0 as there may be variations of each navigator (engine)

  • 1

    Thanks for the tip. It was very important. I will change here, because I had the impression that the 0 an hour failed :|. And the plugin itself imagesLoaded uses in almost all calls the SetTimeOut 0

  • William, if I use this AMD, solves the problem 100%, or I’ll still have to do the little trick of setTimeout 0

  • So it means that it’s like the browser has its own to-do list and, when we use the setTimeout 0, we are simply allocating this task to be executed last?

  • @Wallacemaxters No, it runs after the delay, like this 0 the only difference is that it simulates the multithread, ie there is no way to know which will run first or finish first, Multithreads run the same line :)

  • @Wallacemaxters added a few more details, focused on their own experiences.

Show 2 more comments

6

This happens because you are performing many tasks.

The browser has to do a lot of things, all at once and running javascript is just one of those things. Hence it is understood that it should be executed synchronously, ie one thing at a time as if it were queue and not in parallel. Hence the solution is to "pause" the javascript execution for the thread rendering to happen (setTimeout 0). Although it seems to say "run it here immediately" it really gives the browser the chance to finish doing some things other than javascript that are in the state waiting to finish before continuing to run Javascript.

SOEN - Why is settimeoutfn 0 sometimes Useful?

So what happens in the first block is something like this

  1. renders the html
  2. receives keyboard input in html
  3. renders the html
  4. executes the javascript
  5. renders the html

in the second block occurs the following:

  1. renders the html
  2. receives keyboard input in html
  3. abort the html rendering
  4. executes the javascript
  5. renders the html

0

Because of process precedence. The keydown event calls the browser api asynchronously, the value assignment calls synchronously.

The assignment concludes at a different time. In the case, first calls the keydown event then assign. As the event concluded first, the text did not yet exist in the textfield.

Depending on the performance of your machine (I believe) the result may work.

Modifying to keyup would already solve the problem.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.