How JavaScript Achieves Asynchronicity.
Joshua Onwuemene
Software Engineer (Node.js, Java) | Tech x Health x Growth | Building robust and scalable backend systems for web and mobile applications.
Introduction
Ever wondered how JavaScript, despite being primarily single-threaded, manages to handle blocking tasks seamlessly? In this article, I dive under the hood of the JavaScript runtime and uncover the art of asynchronicity.
The Single-Threaded Model of JavaScript
JavaScript operates on a single (main) thread, which executes all the functions in a JavaScript program. This thread uses a "call stack", a stack data structure, to keep track of the functions in their order of execution.
When a function is called, it is added to the top of the call stack (pushed) and when it completes, it is removed (popped) from the stack, thus following the LIFO (Last In, First Out) principle.
The Call Stack & Synchronous Execution
As operations are added to the call stack, they're executed in a synchronous manner (line by line). Each function call must be completed before moving on to the next, creating a sequential flow of execution.
The single-threaded nature of JavaScript, with its single call stack, simplifies the programming model by ensuring that only one operation is executed at a time. However, this can also lead to blocking if a function takes a long time to execute.
Enter Asynchronous Programming!
To handle time-consuming, blocking tasks without blocking the main thread, JavaScript relies on asynchronous programming, a technique that allows other operations to continue executing without waiting for time-consuming tasks to complete.
领英推荐
The JavaScript runtime uses mechanisms like Web APIs (in the browser) or Node APIs (in the Node.js runtime), callback queues, and the event loop to create an asynchronous environment within JavaScript's overall single-threaded execution model.
How It All Works
When the JavaScript engine encounters an asynchronous operation, it hands it off to a Web or Node API, allowing the main thread to continue its execution without waiting for the asynchronous task to complete. These APIs operate outside the main thread, preventing blocking of its call stack.
Upon completing an asynchronous task, a callback function is placed in a "callback queue", a queue data structure, that stores callback functions after an asynchronous task completed. The callback functions in the queue wait in line to be executed when the JavaScript call stack is empty.
PS: A callback is simply a function that is passed into another function as an argument. In asynchronous programming, callbacks are invoked after an asynchronous task is completed.
The JavaScript runtime uses an "event loop" which loops through the call stack to check if is empty. While the call stack is not empty, operations in the stack continue to execute. But when the call stack becomes empty, it moves callbacks out of the queue into the call stack for execution. The first callback added to the queue is going to be the first callback to leave, thus, following the FIFO (First In, First Out) principle.
This synergy between the call stack, Web/Node APIs, callback queue, and event loop creates a sort of parallelism, enabling JavaScript to efficiently handle time-consuming operations without freezing the main thread.
Was this helpful? Do let me know!
AI Software Engineer | I boost revenue growth for B2B using AI automations
10 个月nice article Joshua Onwuemene