# Understanding Asynchronous Execution with a Queue in JavaScript

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:

```javascript
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:

```javascript
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?

```javascript
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:

```javascript
[T1]
```

`_next()` runs.

Condition:

```javascript
active < limit → 0 < 2 ✅
```

T1 starts.

```javascript
active = 1
```

T1 schedules `setTimeout` and exits immediately.

* * *

### Step 2: Second Task

Queue:

```javascript
[T2]
```

Condition:

```javascript
1 < 2 ✅
```

T2 starts.

```javascript
active = 2
```

Now 2 tasks are running.

* * *

### Step 3: Third Task

Queue:

```javascript
[T3]
```

Condition:

```javascript
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:

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

Now:

```javascript
active = 1
```

Since active < limit:

T3 starts.

```javascript
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:

```plaintext
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:

```js
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.
