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

18

I've been observing for some time that some event types only work correctly when we define a certain function that is called with setTimeout with value 0 . On the contrary, if we call this function without setTimeout , unwanted effects occur.

I'll exemplify with an input that gets 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><inputid="teste-1" />
<input id="teste-2" />

Notice that when we type a text in capital letters in the first% w_ of%, the last character always remains upper case. In the second case, with input , all characters are transformed to lowercase correctly.

I'd like to know why exactly this happens!

Because javascript needs setTimeout 0 to behave as expected - the expected way is the way it works when I use setTimeout .

    
asked by anonymous 27.10.2015 / 14:03

3 answers

15

This is usually used so that errors or long loops within callback do not affect what comes after setTimeout , it is an "simulate" the Multiple threads ( multithread ), is not a thread true, but works on the same line.

With 0 (zero), it will run at the same time that setTimeout is called, but it will not be necessary what comes after setTimeout wait for the process to finish, it is as if the callback were 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's worth noting that every engine (ECMAScript browser technology - 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 errors 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 something like:

  

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

In other words, the second console.log was not affected by the error.

Why not use zero in setTimeout

Does not mean that you will never use, for example just to avoid errors that can be caused within the callback zero will be enough, the situations that we should avoid 0 is when there are processes of the browser itself that can delay, such as rendering images after onload , we often use setTimeout to wait for another process to finish, for example an image inserted by javascript, even using Image.onload yet it did not render within a millionth of second (something imperceptible to human being), then a little delay can help, like:

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

Alternatives to setTimeout

It works fine in most cases, but it should be noted that scripts that are slow to execute will still freeze the webbrowser for some time (depending on the script), even using setTimeout .

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

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

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

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

if ( typeof define === "function" && define.amd ) {
    define( "jquery", [], function() {
        return jQuery;
    });
}
    
27.10.2015 / 14:12
6

This is because you are performing many tasks.

The browser has to do a lot of things, all at once and running the javascript is just one of those things. Hence it is understood that it must be executed synchronously, that is, one thing at a time as if it were queue and not in parallel. Hence the solution is to "pause" the javascript execution so that the rendering of the thread happens ( setTimeout 0 ). Although it seems to say "run this right away" it actually gives the browser a chance to end up doing some non-javascript stuff that is in the waiting state to finish before continuing to run JavaScript.

SOEN - why is settimeoutfn 0 sometimes useful?

So what happens in the first block is something like the following

  • render html
  • receives input from keyboard in html
  • render html
  • runs javascript
  • render html
  • In the second block the following occurs:

  • render html
  • receives input from keyboard in html
  • stop rendering html
  • runs javascript
  • render html
  • 27.10.2015 / 14:20
    0

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

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

    Depending on the performance of your machine (I think) the result can work.

    Changing to keyup would already solve the problem.

        
    12.08.2016 / 16:22