DEV Community

Cover image for Understanding Node.js: Single-Threaded Server-Side Language

Understanding Node.js: Single-Threaded Server-Side Language

Puncoz Nepal
Full-stack Software Engineer based in #Nepal. #AI #ML #Robotics #enthusiast #computer #science. Creating impact by using state of the art technology.
Originally published at ・3 min read

Most of the server-side languages, like PHP, ASP.NET, Ruby, JAVA servers, follow multi-threaded architecture. That means, every request by the client results in the instantiation of a new thread or even a process.

However, in Node.js, all requests are handled in a single thread with shared resources. Then how does Node.js handle concurrent traffic or requests? It follows β€œSingle Threaded Event Loop Model” architecture that runs on top of a single V8 engine instance.

Node.js is event-driven that implements background workers to achieve non-blocking asynchronous behavior. We called it the Observer Pattern. Node thread keeps an event loop and whenever a task gets completed, it fires the corresponding event which signals the event-listener function to execute as illustrated below.

Alt Text

As soon as Node.js starts, it initializes the event loop, processes the provided input script (i.e. initiates variables and declares functions) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.

Alt Text

As shown in the figure above, each phase has a FIFO queue of callbacks to be executed.

Overview of the Phases:
timers: this phase executes callbacks scheduled by setTimeout() and setInterval().

pending callbacks: executes I/O callbacks deferred to the next loop iteration.

idle, prepare: only used internally.

poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); NodeJs will block here when appropriate.

check: setImmediate() callbacks are invoked here.

close callbacks: some close callbacks, e.g. socket.on('close', ...).

More detail on this can be read from the official docs.

Alt Text

As shown in the above block diagram, Node.js listens and passes every concurrent traffic in a queue, which will be executed by an event loop as explained above. Let’s see an example to observe this single-threaded architecture of a Node.js web application.

const app = express()

let visitorCount = 0

app.get("/", (req, res, next) => {

    res.send(`Hello World, visitor counter is: ${visitorCount}`)

const port = 8002
app.listen(port, () => {
    console.log(`Start listening at port: ${port}`)
Enter fullscreen mode Exit fullscreen mode

In the above example, we are using express-js which we need to install from npm. To run the above script, simply type the following command in your terminal.

$ node server.js         // here, server.js is the name of the file
Enter fullscreen mode Exit fullscreen mode

Now, if we browse localhost:8002 in the browser, on every request, the visitorCount gets updated. Isn't that magic? In other programming languages, to achieve this, we will need to store that counter in some persistent storage. Here, as per the output, on every request, the visitorCount variable is being updated. That means, for all requests, Node.js is running the same instance (thread/process) and visitorCount variable is the same for all the requests.

This is how Node.js works. Due to all these architectural implementations of Observer patterns, Node.js is pretty fast compared to similar other server-side languages and technologies.

This article was originally published in YIPL Blog.

Discussion (2)

rlogical profile image
Rlogical Techsoft Pvt Ltd

Great post..

puncoz profile image
Puncoz Nepal Author

Thanks :)