How does asynchronous programming work in Javascript?

Asked

Viewed 7,672 times

32

As far as I know, asynchronous programming in C#, for example, uses the idea of threads. The tasks to be performed can be divided into threads and then run in parallel. I always imagined that Javascript was 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.

It turns out that I later found out that Javascript doesn’t have that. It runs on one thread and there’s no way to distribute tasks on threads. But still asynchronous programming is one of the main points of the language. How this is done in Javascript and how does it relate to promises and callbacks?

1 answer

46


Asynchronous programming is one of the main points of the language precisely because Javascript runs in a single thread. If there is only one thread to execute your code, you need to avoid as much as possible that code blocks the thread. Hence, time-consuming operations such as HTTP requests and disk access, or a database, are typically performed asynchronously.

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

This single thread executes a loop of events (Event loop).

There is a separate component responsible for popular event queue, popularly called Event Pump ("event pump", because "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 each other. The Event loop is responsible for executing the Javascript code, processing one event at a time, according to 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.
}

Every iteration of the loop (tick), the engine check if there is an event in the queue. In the browser, this can be a timer expired, an XHR request response, or an interface event (such as an Event Listener one-click on some 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 callback is executed synchronously. Each tick loop has its own function execution stack. At the end of each tick the stack is always empty.

It is easier to understand with an example. Consider the following code, which creates a timer whose callback changes the value of the variable x and shall be executed within 500 milliseconds:

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

This is processed as follows:

Tick 0: Empty event queue. Creates variables x and i; assign value 0 to x; schedules the timer callback to run at 500ms; executes the body of the loop for 100 times.

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

(...)

Tick N: 1 event (expired timer) in the queue. Executes the pending callback, ie assigns value 10 to the variable x captured via closure.

Note that there is no guarantee that the callback of our timer will run exactly 500ms after the start of the timer. It will run at the first possible opportunity when the condition is satisfied. For example, if the body of the loop for perform some slow operation that takes 600ms to perform, our callback will run on tick 1, about 600ms after scheduling (ie delayed compared to scheduling).

With promises the engine is exactly the same, since promises in Javascript are just a more complex way, but with cleaner syntax, to use callbacks. Nor were they a native construction of the language in Ecmascript 5, only included in the following versions.

References

  • 1

    Excellent answer. Some comments: "In Javascript on the server there are still other cases, such as asynchronous access to the disk or a database." - even if its use is a little more unusual, we have the Indexeddb and File System API that provide very similar functionality on the front end as well. (cont)

  • "[Promises] They are not a native construction of language." - Promises are currently advancing in the standardization process. The latest versions of Firefox, Chrome and Opera already have native implementations (still experimental, of course).

  • 1

    Thanks for the comments, @Fabríciomatté. I had really forgotten about Indexeddb and FS API. But I think they can be left out (I just mentioned them as examples), as I did with the Web Workers, which are a separate case (they work as separate threads, whether or not they are implemented as such - I don’t know how it actually works). The native Promises predicted for ES6 I also left out intentionally, not to lengthen in response.

  • Yes, the IDB/FS part was for observation purposes only, but the Promises part could have a slight rewrite. " They are not a native construction of the language, "seems a little wrong/outdated to me, as Promises are currently being natively incorporated into the language. If I’m not mistaken, TC39 has already agreed to incorporate Promises into ES6.

  • 1

    @I don’t think it’s wrong, I’m talking about ES5. I might even add that this is beginning to be implemented natively, and that it should be part of the next version of the specification. But I’m not sure how it will work (I’m behind on my ES6 studies). What if it’s something like the "classes" of ES6, which will be just syntax sugar for prototypical heritage? I will study the link you have passed and see what I can improve.

  • Yes, an observation that will be part of ES6 (perhaps worth mentioning that there are already experimental implementations by browsers, as well as polyfills) is already of good size, there is no need for a doctoral thesis on Promises ;). Good, but if you want to talk about Promises, you’ve worked with jQuery.Deferred (or objects jqXHR)? Follows the same line, only that Promises allow chaining and better error handling.

  • And in my opinion, Promises will gain a lot more popularity when the Async Functions of ES7 are implemented. =]

  • 1

    Ah, the HTML5 Rocks tutorial about Promises is much more detailed about concepts and usage than the MDN page that Linkei previously.

  • 1

    Thanks @Fabríciomatté, I have some reading for the rest of the week :) I’ve worked a lot with $.Deferred, and I played a little with Q, but I want to see how this will be specified in ES6. I have the impression that it will not be treated differently in the loop Event. I tweaked the last sentence a little to clarify that files are predicted in ES6 and implemented experimentally by some browsers.

  • Perfect! :D And yes, I believe that you should not change anything in Event loop -- Promises are just an abstraction level for asynchronous code. The Q library implements the Promises/A+ standard that is the basis of the ES6 Promises specification, so you should already know a good deal about how Promises work. =] I have to find time to study them further also, for now I continue implementing my TCC.

Show 5 more comments

Browser other questions tagged

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