Course contentsShow
← Node.js
Lesson 236 of 1,9803. Asynchronous Patterns and Flow ControlFree lesson

Ordered Sequential Callback Queues

You will build an execution queue that enforces strict sequencing of non-promise-aware asynchronous tasks.

In this lesson, you will learn how to guarantee that independent asynchronous callback tasks run one after the other, ensuring predictable execution order even when they don’t return Promises.

The Problem: Chaos in Asynchrony

When you trigger multiple asynchronous tasks (like reading files or hitting an API), Node.js usually executes them as soon as they are ready. If Task A takes longer than Task B, Task B might finish first. If your logic relies on Task A finishing before Task B starts, this "race condition" can crash your application.

The Solution: The Sequential Queue

To fix this, we build a "Queue" that holds onto our functions. Instead of firing them all at once, the queue triggers the next task only when the current one notifies us it is finished via a callback.

Imagine a line at a checkout counter; the next person only steps forward once the previous person is done. Here is how we implement that in plain Node.js:

const taskQueue = {
  queue: [],
  isProcessing: false,

  push(task) {
    this.queue.push(task);
    this.process();
  },

  process() {
    if (this.isProcessing || this.queue.length === 0) return;

    this.isProcessing = true;
    const nextTask = this.queue.shift();

    // The callback signals the task is done
    nextTask(() => {
      this.isProcessing = false;
      this.process(); // Trigger the next one
    });
  }
};

// Usage
taskQueue.push((done) => setTimeout(() => { console.log('Task 1'); done(); }, 1000));
taskQueue.push((done) => setTimeout(() => { console.log('Task 2'); done(); }, 500));

How it Works

  1. The Array: We store our callback functions in a simple queue array.
  2. The Lock: isProcessing acts as a gatekeeper. If it’s true, the queue waits.
  3. The Trigger: When a task completes, it calls the done function we provided. This clears the lock and immediately calls process() again to check for the next job.

By wrapping our asynchronous work in this structure, we move from unpredictable, concurrent execution to a reliable, serial timeline.

Key Takeaway: An ordered sequential queue uses a recursive callback trigger and a processing lock to force asynchronous tasks to execute strictly one at a time.