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.
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.
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.
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
.
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.
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?
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.
While executing this code, the Call stack
performs following tasks:
main
code are stacked on the stack
.helloJsConf
function is called and stacked on the stack
.hello
function is called and stacked on the stack
.console.log ('hello')
is stacked on the stack
.stack
.hello
function is removed from the stack
. 7.console.log ('JSConfKorea')
is stacked on the stack.helloJSConf
function is also removed from the stack
after finishes the job.main
block of code is removed from the stack
.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?
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)