Concurrency and Multithreading in JavaScript: From A to Z

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: From A to Z



JavaScript is historically a single-threaded language operating within the paradigm of event-driven programming. However, modern JavaScript (especially in the Node.js environment) offers powerful tools for working with concurrency and multithreading. In this article, we will break down how Event Loop, Promises, async/await, Worker Threads, and clustering work, and how to use these mechanisms to improve the performance of your applications.



1. Event Loop: The Foundation of Concurrency in JavaScript



The Event Loop is the heart of JavaScript's asynchronous model. It allows handling multiple operations without blocking the main execution thread. In Node.js, the Event Loop manages task queues (macrotasks and microtasks), ensuring non-blocking I/O.



Example of Event Loop operation:



console.log('Start');

setTimeout(() => { console.log('Timer 1');}, 0);

process.nextTick(() => { console.log('nextTick');});

Promise.resolve().then(() => { console.log('Promise');});

console.log('End');

// Output:// Start// End// nextTick// Promise// Timer 1


It is important to understand the execution order: synchronous code → microtasks (process.nextTick, Promise.then) → macrotasks (setTimeout, setInterval).



2. Promises and async/await: Managing Asynchrony



Promises are objects representing the result of an asynchronous operation. They help avoid "callback hell" and allow building call chains. async/await is syntactic sugar over Promises, making asynchronous code look synchronous.



Example with async/await:



const fetchData = async (url) => {  const response = await fetch(url);  if (!response.ok) {    throw new Error(`HTTP error: ${response.status}`);  }  return await response.json();};

const main = async () => { try { const data = await fetchData('https://api.example.com/data'); console.log('Received data:', data); } catch (error) { console.error('Error:', error.message); }};

main();


This approach allows executing multiple asynchronous operations in parallel using Promise.all():



const [user, posts] = await Promise.all([  fetchUser(userId),  fetchPosts(userId)]);console.log(user, posts);


3. Worker Threads: True Multithreading in Node.js



Starting from Node.js 10.5.0, the worker_threads module provides the ability to create real threads for CPU-intensive tasks. Each Worker runs in a separate thread with its own V8 instance.



Example of creating a Worker:



// main.jsconst { Worker } = require('worker_threads');

const runWorker = (workerData) => { return new Promise((resolve, reject) => { const worker = new Worker('./worker.js', { workerData });

worker.on('message', resolve); worker.on('error', reject); worker.on('exit', (code) => { if (code !== 0) { reject(new Error(`Worker stopped with code ${code}`)); } }); });};

const main = async () => { const result = await runWorker({ iterations: 1000000 }); console.log('Result:', result);};

main();


// worker.jsconst { parentPort, workerData } = require('worker_threads');

const heavyComputation = (iterations) => { let result = 0; for (let i = 0; i < iterations; i++) { result += Math.sqrt(i); } return result;};

const result = heavyComputation(workerData.iterations);parentPort.postMessage(result);


Worker Threads are ideal for tasks that block the Event Loop: image processing, mathematical calculations, parsing large files.



4. Clustering: Scaling Across Multiple Processes

\

Blogs

Book Recommendations