Stacks and Queues
Stack (LIFO: push/pop) and queue (FIFO: push/shift). Arrays are great stacks but poor queues because shift is O(n); use a linked-list queue when throughput matters.
A stack is a last-in-first-out (LIFO) collection: the last item you push in is the first one you get back, like a pile of plates. A queue is first-in-first-out (FIFO): items leave in the order they arrived, like a line at a coffee shop. Both are abstract interfaces, not specific data structures - JavaScript arrays can implement either.
A stack on top of an array uses push to add to the top and pop to remove from the top. Both are O(1): they touch only the last element, so the array never has to shift anything around. A classic use case is an undo history: each action goes on the stack; pressing undo pops the most recent one off.
const undoStack = [];
function doAction(action) {
undoStack.push(action);
console.log("Did:", action);
}
function undo() {
const last = undoStack.pop();
if (last) {
console.log("Undid:", last);
} else {
console.log("Nothing to undo");
}
}
doAction("type 'hello'"); // Did: type 'hello'
doAction("bold selection"); // Did: bold selection
undo(); // Undid: bold selection
undo(); // Undid: type 'hello'
undo(); // Nothing to undoA queue on top of an array uses push to add to the back and shift to remove from the front. The shift operation is O(n) because the browser has to move every remaining element one index earlier. For small queues that's fine; for a hot queue (many items, high throughput) the cost adds up.
const printQueue = [];
function enqueue(job) {
printQueue.push(job);
}
function dequeue() {
return printQueue.shift(); // O(n) - the whole array slides left
}
enqueue("page-1.pdf");
enqueue("page-2.pdf");
enqueue("page-3.pdf");
console.log(dequeue()); // "page-1.pdf"
console.log(dequeue()); // "page-2.pdf"
console.log(dequeue()); // "page-3.pdf"
// Checking length and front without removing
const taskQueue = ["alpha", "beta", "gamma"];
console.log(taskQueue.length); // 3
console.log(taskQueue[0]); // "alpha" (peek at front without shift)In production
Arrays make great stacks (push and pop are O(1)) but lousy queues: shift has to slide every element one position forward, so it's O(n). The regression is easy to miss in a benchmark that uses 100 items - it only shows up around 100,000. For a hot queue, reach for a linked-list-backed queue or a circular buffer where both enqueue and dequeue are O(1). Most job-queue and event-loop implementations inside Node.js internals use a circular buffer exactly for this reason.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free