Skip to main content

Command Palette

Search for a command to run...

Understanding Asynchronous Execution with a Queue in JavaScript

Updated
4 min read
Understanding Asynchronous Execution with a Queue in JavaScript
S

I'm Shubham (@shubhamsinghbundela), I'm a Software Engineer, a Full-stack developer, a tech enthusiast, and a technical writer here on @Hashnode. I have a strong zeal to share my acquired knowledge and I am also willing to learn from others.

When we say JavaScript is asynchronous, what does that really mean?

It means:

A task can start now and finish later, without blocking other code.

But when we have multiple async tasks, we need control.

What if:

  • We have 100 API calls?

  • Or 100 file uploads?

  • Or 100 video processing jobs?

If we run all of them at once, we may overload the system.

So we use a queue with concurrency control.


The Goal

We want to:

  1. Execute asynchronous tasks.

  2. Allow only N tasks to run at the same time.

  3. Queue extra tasks.

  4. Start queued tasks automatically when one finishes.

  5. Call a callback when each task completes.

Here is the implementation:

class CallbackPool {
  constructor(limit) {
    this.limit = limit;        
    this.queue = [];          
    this.active = 0;          
  }

  run(task, onComplete) {
    this.queue.push({ task, onComplete });
    this._next();
  }

  _next() {
    while (this.active < this.limit && this.queue.length > 0) {
      const { task, onComplete } = this.queue.shift();

      this.active++;

      task((err, data) => {
        this.active--;

        if (onComplete) {
          onComplete(err, data);
        }

        this._next();
      });
    }
  }
}

The Asynchronous Task

Here’s our async task:

const task = (cb) => {
  setTimeout(() => {
    cb(null, "done");
  }, 20);
};

This is asynchronous because:

  • setTimeout schedules work.

  • It does NOT block.

  • It returns immediately.

  • The callback runs later (after 20ms).


What Happens When We Run 5 Tasks?

const pool = new CallbackPool(2);

for (let i = 0; i < 5; i++) {
  pool.run(task, () => console.log("Task Done"));
}

Concurrency limit = 2

Let’s simulate step by step.


Step 1: First Task

Queue:

[T1]

_next() runs.

Condition:

active < limit → 0 < 2 ✅

T1 starts.

active = 1

T1 schedules setTimeout and exits immediately.


Step 2: Second Task

Queue:

[T2]

Condition:

1 < 2 ✅

T2 starts.

active = 2

Now 2 tasks are running.


Step 3: Third Task

Queue:

[T3]

Condition:

2 < 2 ❌

T3 stays in queue.


Important Understanding

At this moment:

  • T1 and T2 are waiting for their 20ms timer.

  • JavaScript is NOT blocked.

  • The program continues.

This is asynchronous execution.


After 20ms (Event Loop in Action)

T1 finishes.

Its callback runs:

this.active--;
this._next();

Now:

active = 1

Since active < limit:

T3 starts.

active = 2
Queue: [T4, T5]

The Pattern

Every time a task finishes:

  1. active decreases.

  2. Completion callback runs.

  3. _next() checks if another task can start.

  4. Next queued task begins.

So execution becomes:

Start 2 →
One finishes →
Start next →
Repeat

Why This Explains Asynchronous Behavior Clearly

This example shows:

  • Tasks do not complete immediately.

  • The system reacts when callbacks fire.

  • The queue stores waiting tasks.

  • The active counter controls concurrency.

  • Scheduling happens dynamically.

This is not parallel threads.

This is event-loop-driven asynchronous scheduling.

More Visualization below:

Time →
--------------------------------------

T1: |------20ms------|
T2: |------20ms------|
T3:          |------20ms------|
T4:          |------20ms------|
T5:                   |------20ms------|

Maximum running at once = 2

Core Concept

Asynchronous + Queue works like this:

  • Add task → goes into queue.

  • If slot free → start immediately.

  • If not → wait.

  • When task finishes → free slot.

  • Start next task automatically.

The key is:

Completion triggers the next execution.

More from this blog

Shubham Tech. Blog's

55 posts

Problem Solver | Currently Working As a Full Stack Developer, Community Leader At @Dev_Matrix | Previously Contributor at @RealDevSquad, @TeamShiksha