Concurrency and Multithreading in JavaScript: A Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Concurrency and multithreading in JavaScript: how to make code run faster



JavaScript has historically been a single-threaded language, but this does not mean it cannot handle multiple tasks simultaneously. In modern Node.js and browsers, there are powerful mechanisms for working with concurrency and multithreading. Understanding these concepts is critically important for creating high-performance server and client applications.



In this article, we will break down how concurrency differs from parallelism, how the Event Loop works, when to use Worker Threads and Web Workers, and also look at practical examples in Node.js.



1. Event Loop and non-blocking I/O



The foundation of concurrency in JavaScript is the Event Loop. This is a mechanism that allows asynchronous operations (e.g., reading files, database queries) to be executed without blocking the main thread.



Imagine a queue: the Event Loop takes a task from the queue (call stack), executes it, and if it encounters an asynchronous operation (e.g., setTimeout or fetch), it passes it to the Web API (in the browser) or libuv (in Node.js). After the operation completes, its callback is placed into the task queue. The Event Loop constantly checks if the stack is empty, and if it is, it takes the next task from the queue.



Example of asynchronous code:



const fs = require('fs');

console.log('Start');

fs.readFile('example.txt', 'utf8', (err, data) => { if (err) throw err; console.log('File read:', data);});

console.log('End');

// Output:// Start// End// File read: ...


Notice: console.log('End') executes before the file is read. This is non-blocking I/O — the main thread does not wait for the operation to complete, but continues execution.



2. Web Workers and Worker Threads: true multithreading



While the Event Loop handles I/O operations well, it does not help with executing "heavy" computations (image processing, cryptography, complex sorting). If you run such code in the main thread, the entire interface (or server) will "freeze" until the computation finishes.



To solve this problem, there are Web Workers (in the browser) and Worker Threads (in Node.js). They allow running JavaScript code in separate operating system threads.



Example with Worker Threads in Node.js



Let's create a file worker.js:



const { parentPort } = require('worker_threads');

// Heavy task: calculating the sum of numberslet sum = 0;for (let i = 0; i < 1e9; i++) { sum += i;}

// Send the result back to the main threadparentPort.postMessage(sum);


Main file main.js:



const { Worker } = require('worker_threads');

console.log('Main thread: starting worker');

const worker = new Worker('./worker.js');

worker.on('message', (result) => { console.log('Result from worker:', result);});

worker.on('error', (err) => { console.error('Worker error:', err);});

worker.on('exit', (code) => { if (code !== 0) { console.error(`Worker exited with code ${code}`); }});

console.log('Main thread: continuing work...');

// Output:// Main thread: starting worker// Main thread: continuing work...// Result from worker: 499999999500000000


As you can see, the main thread is not blocked, and the worker performs computations in the background. This is true multithreading.



3. Async/Await and Promise: managing concurrency



Modern JavaScript offers elegant ways to work with asynchronous code — async/await and Promise. They do not create new threads, b

Blogs

Book Recommendations