All You Need To Know About Event Loop (1)

JavaScript Process

One of the great features of JavaScript is that it is a single-threaded-based language. Single thread means that only one task can be processed at the same time. However, if you consider the environment in which JavaScript is actually used, you can see that a lot of work is being processed at the same time.

For example, a web browser processes mouse input while displaying an animation effect, on the other hand, a Node.js based web server handles multiple HTTP requests simultaneously. How could this be possible with a single thread? In other words, “How does JavaScript support Concurrency?”

The concept that emerges this time is the Event Loop. When introducing Node.js, you’ve probably seen phrases like ” Non-Blocking I/O support in an event loop-based asynchronous approach… “. That said, JavaScript supports asynchronous I/O and concurrency by using event loops.

Node.js with this event loop could be hard for someone familiar with using other languages in a synchronous way such as Java. Not only that, even those who have been using the service for a long time and are familiar with programming how to use it are not know in detail how the event loop actually works.

There is no event loop in ECMAScript.

Even if you search through JavaScript-related books, it is surprisingly difficult to find a description of the event loop. The reason is probably that ECMAScript specification doesn’t really have anything about event loops.

To be more specific, you can say “ECMAScript doesn’t mention concurrency or asynchronous” ; technically, it has changed slightly since ES6, but I’ll explain more later. In fact, JavaScript engines like V8 use a single Call Stack, and each time a request comes in, the request is sequentially placed on the call stack and processed.

How are asynchronous requests made and what handles concurrency? The answer is a JavaScript engine runtime environment: browser or Node.js is responsible for asynchronous work.
Check a simple diagram of the browser environment below.


browser environment

As you see, functions such as setTimeout() and XMLHttpRequest() are actually used for asynchronous calls are defined separately in the Web API area, not in the JavaScript engine. Furthermore, Event loops and Task queues are also implemented outside of the JavaScript engine. Next is the Node.js environment.


nodejs environment

In the diagram above, Node.js structure is similar to the browser’s environment. As well known, Node.js supports asynchronous I/O using libuv library and this libuv provides an Event loop.
The JavaScript engine calls the API of Node.js for asynchronous operation, then the callback is scheduled and executed through the event loop of libuv.

In brief, “JavaScript is a single thread-based language” is true only in terms of “JavaScript engines that use a single call stack”.
In environments where JavaScript is actually executed (browser, ‘Node.js’, etc.), it mainly uses multi-threading. Thus, in this environment, the device needed to cooperate with the JavaScript engine that uses a single call stack is called Event loop.

Single Call Stack and Run-to-Completion

Run-to-completion means that once the message processing begins, no other action can’t intervene until the message processing is completed. Here is an example of Run-to-completion below.


runtocompletion

Running the example code on the left side above, can get the result as on the right side above. (browser process may be hanged and need to be forced to close). How does this Run-to-completion method work?

Call Stack

In the JavaScript engine there is a place called the Call Stack that acts as a cursor indicating the location of the running code.
Whenever a request comes in, the request is sequentially placed on the Call Stack and processed.

It controls which functions are currently called and running, which functions should be called next, etc. What happens in the call stack when the JavaScript code is executed?
check the example code below.

Basic Call Stack flow


callstack

While executing this code, the Call stack performs following tasks:

  1. Entire blocks of main code are stacked on the stack.
  2. The helloJsConf function is called and stacked on the stack.
  3. The hello function is called and stacked on the stack.
  4. console.log ('hello') is stacked on the stack.
  5. Printing ‘hello’ to the console removes ‘console.log (‘hello’)’ from the stack.
  6. The hello function is removed from the stack. 7.console.log ('JSConfKorea') is stacked on the stack.
  7. Printing ‘JSConfKorea’ on the console will remove ‘console.log (‘JSConfKorea’)’ from the stack.
  8. The helloJSConf function is also removed from the stack after finishes the job.
  9. The main block of code is removed from the stack.
  10. Like this, JavaScript runs with Call stack structure in Run-to-completion mechanism.

In the same situation, what if encountering a task that takes a long time to process requests one by one?


problem

If there is a request that takes a long time to process, such as the ’someExpensive’ function during the action, the ’hello’ or ’jsConfKorea’ message is delayed to display.

‘JavaScript’ is said to treat operations as a Single Call Stack structure. Think about when using web services. While clicking, scrolling, and typing, data is called up and displayed on the screen.
Are those jobs actually being processed in order and waiting in turn?

Not really. Browsers and JavaScript engines provide web APIs (setTimeout, Promise, etc...) and Event Loops to solve this concurrency problem.

Continued … All You Need To Know About Event Loop (2)