Getting Started With Queues in Node.js
In this tutorial, we’ll cover queues in JavaScript. We’ll define what a queue is, look at scenarios where a queue is useful, write code to demonstrate how queues work, and explore the different parts of the code structure.
In this tutorial, we’ll cover queues in JavaScript. We’ll define what a queue is, look at scenarios where a queue is useful, write code to demonstrate how queues work, and explore the different parts of the code structure.

Ikeh Akinyemi
Ikeh is a Software Engineer based in Rivers State, Nigeria. He is interested in the studies that implement Pure and Applied Mathematics in Software Engineering. Passionate about Open Source.
In this tutorial, we’ll cover queues in JavaScript. We’ll define what a queue is, look at scenarios where a queue is useful, write code to demonstrate how queues work, and explore the different parts of the code structure.
Applications often need to process workloads asynchronously, outside the main application flow. These workloads may also contain other asynchronous actions, which can become difficult to manage in Node.js. Queueing is a technique that helps handle these operations more easily and effectively by processing tasks in an organized sequence.
Table of Contents
- Prerequisites
- Introduction to Queues
- Introduction to the Types of Callback Queues
- Implementing a Queue in JavaScript Using OOP
Prerequisites
- Basic understanding of JavaScript
- Know the difference between synchronous programming and asynchronous programming
- Basic understanding of Node.js
Introduction to Queues
A data structure can be defined as a group of data elements that provides an efficient way to store and organize data in a computer so it can be retrieved and used efficiently.
A queue is also a linear data structure. Linear data structures have data elements arranged in an orderly, sequential manner, where each element is connected to its previous and next element. This connection helps us traverse the linear data structure in a single flow.
In Node.js, a queue can be used to organize asynchronous operations appropriately and progressively. It is an ordered list of asynchronous operations where a new operation is inserted at the end of the queue and removed from the front.
These operations can take different forms, including HTTP requests, file read or write operations, streams, and more. Handling asynchronous operations within Node.js applications can be difficult to manage, especially when those operations take an indefinite amount of time to complete.
With queues, we can manage asynchronous operations in a structured way by inserting new elements and removing existing elements from an array. The call stack, event loop, and callback queues all help implement queue behavior within a program.
The call stack records and tracks the current function being executed and where the execution is happening from. A function gets added to the call stack when it is about to be executed within the program.
The event loop allows a Node.js application to perform non-blocking I/O operations, even though JavaScript is single-threaded. Whenever possible, operations are offloaded to the system kernel. The event loop constantly checks whether the call stack is empty, then takes a function from the callback queue and adds it to the call stack. The event loop only checks the queues after all synchronous operations have been executed.
Callback queues store callback functions from asynchronous operations once those operations have been completed behind the scenes.
Introduction to the Types of Callback Queues
Before we introduce the types of queues available, we’ll quickly go over their order of priority, then discuss each one in that order. There are timer, close, I/O, microtask, and check queues. The microtask queue has the highest priority, followed by the timer queue, the I/O queue, the check queue, and, lastly, the close queue.
Microtask Queue
Within this type of callback queue, the event loop constantly checks for delayed functions before proceeding to other queues. The queue follows a first-in, first-out structure, meaning tasks that are enqueued first run first. We can implement this with two helpful functions:
-
process.nextTick: This function executes another function on the next tick, or the next iteration of the program. The microtask queue stores these functions so they can run before the event loop moves on to other queues. This queue is processed first. -
Promise: A promise represents the eventual completion or failure of an asynchronous operation. Promise handling is always asynchronous because promise callbacks pass through the internal promise jobs queue, also known as the microtask queue.
Timer Queue
Node.js provides us with a Timers module, containing functions that execute a program after a set period of time. This provides us with the means to have time-related operations that are asynchronous in nature.
setTimeout(() => {
//Executed last
...
}, 0);
console.log("Executed first");
The event loop executes all synchronous operations first. Once the call stack is empty, it returns to the callback queues to process asynchronous operations.
I/O Queue
Here, we’re dealing with operations that interact with external environments outside the internal program. These operations are handled asynchronously by Node.js after the synchronous code has finished running.
Examples include file read and write operations, network operations, and other tasks that reach outside the program to fetch or process data. The I/O callback queue holds these completed operations until the event loop moves them to the call stack for execution.
Check Queue
The check queue allows callback functions scheduled with setImmediate() to run after the I/O queue callbacks have been executed. With this queue, the event loop checks whether the call stack is idle and whether any scripts have been queued with setImmediate(). If so, it may continue by executing callbacks from the check queue instead of waiting for another timer phase.
setImmediate() works like a special timer that runs in a separate phase of the event loop. It uses a libuv API to schedule callbacks after the poll phase has been completed.
Close Queue
This queue stores functions associated with close event operations. One example is a stream, which is an abstract interface for working with streaming data in Node.js. For instance, a stream emits a close event when it has been closed, signaling that no more events will be emitted. Similarly, an HTTP server can emit a close event when the server closes.
Implementing a Queue in JavaScript Using OOP
We’ll implement the FIFO (first in, first out) nature of queues as a data structure. The basic concept behind this implementation is adding elements to the end of the queue and removing elements from the front. In JavaScript, arrays can be used to implement queues.
Let’s explore it:
Class Queue {
constructor() {
this.list = [];
}
}
We just created a Queue class that contains an array called list, which represents the basic structure of a queue. The constructor function initializes the array that will be used for the queue in this program.
Next, we’ll define the different methods that belong to the queue data structure, using conventional names for each one.
The first method adds an element to the end of the queue. We’ll implement this using the Array.prototype.push() method.
Class Queue {
...
enqueue = (el) => {
this.list.push(el);
}
}
In the code above, we define a function named enqueue that accepts an argument. This argument is added to the end of the list array.
The next function removes elements from the front of the queue. We’ll implement this functionality using the Array.prototype.shift() method.
Class Queue {
...
dequeue = () => this.list.length !== 0 ? this.list.shift() : “No executable element”;
}
We implemented a dequeue function within the Queue class. This function first checks whether the array is empty. If the array length is not equal to zero, it then runs the shift method to remove an element from the front of the array, or queue.
Next, we’ll define the helper functions for the Queue class we just created. These helper functions will be useful when working with the queue data structure.
This function returns the element at the position passed in as an argument:
Class Queue { ...
queueIndex = (index) => this.list.length !== 0 ? this.list[index] : "No executable element";}
For the method above, we pass in the position of the element we want to retrieve while making sure the array is not empty.
We have repeatedly needed to check whether the array is empty before carrying out an operation on the queue. To solve this, we’ll implement a helper function that checks the queue’s emptiness before any operation runs:
Class Queue {
...
isEmpty = () => this.list.length !== 0;
}
With this function, we can restructure our Queue methods to use it whenever we need to check whether the list array is empty.
The next function depends on the purpose the queue will serve within our application or program.
Class Queue {
...
execAll = () => {
this.list.forEach(item => item())
}
}
Our program is designed to execute asynchronous operations in a more synchronous-looking order. In this case, the elements within the queue are functions that will be executed within the program.
We’ll use the queue and the methods defined within it to demonstrate a simple program that executes functions stored in the queue.
const queue = new Queue();
//add an element
queue.enqueue(() => "Hello world program");
queue.enqueue(() => "Demo program");
queue.enqueue((a, b) => {
switch (a) {
case a === b:
return `${a} and ${b} are absolute equals`;
case a !== b:
return `${a} and ${b} are absolute unequal`;
default:
return "No data passed in";
}
});
queue.enqueue(() => ({
first_name: "Amanda",
last_name: "Petes",
email: "adeva@adevait.com",
}));
queue.dequeue();
let executable = queue.queueIndex(2);
console.log(executable());
queue.execAll();
console.log(queue);
/**{
first_name: 'Amanda',
last_name: 'Petes',
email: 'adeva@adevait.com'
}
Demo program
No data passed in
{
first_name: 'Amanda',
last_name: 'Petes',
email: 'adeva@adevait.com'
}
Queue {
enqueue: [Function: enqueue],
dequeue: [Function: dequeue],
queueIndex: [Function: queueIndex],
isEmpty: [Function: isEmpty],
execAll: [Function: execAll],
list: [
[Function (anonymous)],
[Function (anonymous)],
[Function (anonymous)]
]
} */
In the code above, we created a queue variable using the Queue class. We then filled this queue with functions, or operations, that will be executed in sequence.
These functions are assumed to be asynchronous operations. We added them to the queue using the enqueue method on the Queue class. We then removed an element from the front of the queue using the dequeue method.
Next, we used the queueIndex method and passed 2 as its parameter. This returned the element, or function, at index position 2.
We saved this anonymous function in a variable called executable and then executed it. Finally, we called the function that executes all functions within the queue. The execAll method loops over all elements within queue.list and calls each one.
FAQs
Q: What are queues in NodeJS?
Queues in Node.js are data structures used to manage asynchronous operations. They help in scheduling tasks, handling background jobs, and managing workloads efficiently. Node.js uses queues to process tasks like I/O operations to ensure a non-blocking, smooth execution of concurrent tasks.
Q: How many queues are in NodeJS?
Node.js has multiple queues, including the main event loop queue, microtask queue, and various internal queues for I/O operations, timers, and other asynchronous tasks. These queues work together to manage and execute tasks in an efficient, non-blocking manner.
