How does asynchronous programming work in JavaScript?

27

As far as I know, asynchronous programming in C #, for example, uses the idea of threads. The tasks to be executed can be divided into threads and can then be executed in parallel. I've always thought that JavaScript is the same and that, for example, when we call the function Q.defer we are instructing that that code should be transferred to another thread.

I happened to find out later that JavaScript does not have this. It runs on a thread only and has no way to distribute tasks in threads. But still, asynchronous programming is one of the main points of language. How is this done in JavaScript and what is the relationship with promises and callbacks?

    
asked by anonymous 24.05.2014 / 16:55

1 answer

38

Asynchronous programming is one of the main points of the language precisely because JavaScript runs on a single thread. If there is only one thread to run your code, you should avoid to the maximum that this code blocks the thread. Therefore, delayed operations such as HTTP requests and disk access, or a database, are typically executed asynchronously.

It is good to clarify that JavaScript code is always processed by a single thread, but that does not mean that the engine language and its host application use a thread. For example, if a Node.js application requests disk access, Node may well use another thread to perform such access. But the code that requests this access and the callback code that handles the result executes on that single thread dedicated to JavaScript code.

This single thread runs a loop event ( event loop ).

There is a separate component responsible for popular event queue , popularly called event pump ("event pump" because it "pumps" events to the queue) . According to Wikipedia , it is typically implemented as a separate thread. Therefore, event pump and event loop operate asynchronously in relation to one another. The event loop is responsible for running JavaScript code, processing one event at a time, depending on the queue.

The loop of events would be something like this:

while(true) {
    // Existem eventos na fila?
    // Se sim, pega o código do primeiro da fila e executa de maneira síncrona.
}

At each iteration of the tick loop, the engine checks to see if there is an event in the queue. In the browser, this can be an expired timer, an XHR request response, or an interface event (such as an event listener from a click on an element). In JavaScript on the server there are still other cases, such as asynchronous access to the disk or to a database. If there are events in the queue, the first of the queue is processed, and the corresponding callback runs synchronously. Each tick of the loop has its own function execution stack. At the end of each tick the stack is always empty.

It's easier to understand with an example. Consider the following code, which creates a timer whose callback changes the value of the x variable and should run in 500 milliseconds:

var x = 0, i;
setTimeout(function() {
    x = 10;
}, 500);
for(i=0; i<100; i++) {
    // faz algo
}

This is rendered as follows:

  

Tick 0: Empty event queue. Creates the variables x and i ; assigns value 0 to x ; schedules the timer's callback to run at 500ms; executes the loop body with% 100 times.

     

Tick 1: Empty event queue (500ms not passed). It does nothing.

     

(...)

     

Tick N: 1 event (expired timer) in the queue. Performs callback pending, that is, assigns value for to variable 10 captured via closure .

Please note that there is no guarantee that our timer's callback will exactly 500ms after the timer starts. It will run at the earliest possible opportunity when the condition is satisfied. For example, if the body of the loop x performs some slow operation that takes 600ms to execute, our callback will run at tick 1 , about 600ms after the schedule (ie delayed scheduling).

With promises the mechanism is exactly the same, since promises in JavaScript are just a more complex but cleaner syntax way to use callbacks. They are not a native construct of the language in ECMAScript 5, but will probably be in the next version (ES6), and are already implemented experimentally in some browsers.

References

24.05.2014 / 19:17