Creating Async Task Runner with Concurrency in JavaScript
Tags: javascript, async, concurrency, scheduler
Published: 2022-11-29 19:46:09


Let us consider we need to create an aync task runner in javascript with the following constraint:

1. The task runner should have a set of tasks that will be pushed to it,
2. Each of these tasks will be some async operation,
3. The task runner should also ensure that at a single point of time only a given number of tasks can perform, and other tasks keep on waiting unless their turn comes. 

Let's code out the solution first

```
class Runner{
      constructor(concurrency=1){
          this.concurrency = concurrency;
          this.waitList = [];
          this.count = 0;
          this.currentQ = [];
      }
      push(task){
         this.waitList.push(task);
         this.run();
      }
      run(){
         let current = this;
         if(this.count<this.concurrency){
            this.count++;
            if(this.waitList.length>0){
               let task = this.waitList.shift();
               let id = task.id;
               this.currentQueue.push(id);
               this.showRunningTasks();
               let done = function(){ 
       this.currentQueue.splice(this.currentQueue.indexOf(id),1);
                  this.showRunningTasks();
                  this.count = this.count - 1;
                  this.run();
               }.bind(current);
               task.task(done);
            }
         }
      }
     
      showRunningTasks(){
         let existingQueue = this.currentQueue.join(", ");
         document.getElementId("running").innerHTML = existingQueue;
      }
}

```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4vqrjk2rxjkx1j9bhrz.gif)

Let's understand this piece of code line by line. 

1. Initially we pass the `concurrency `flag indicating how many tasks can run concurrently. The `waitList` is used for making a wait list of tasks, which will wait unless the queue referred to as `currentQueue` is empty. The `count` variable is initialised to count the number of concurrent tasks at a moment.

2. The `push ` method is used to push a task to `waitList`, and we then execute the run method.This simply indicates that the tasks need to be run if the queue is empty

3. The run method is interesting. First it preserves the context of the task by the virtue of first line denoted as `let current = this`

4. Next, we check for the concurrency and if the waitList has elements, we simply,

-   push the task id to the currentQueue, 
-  display the current running tasks via the method `showRunningTasks`,
-   in the inner `done `method, which will be taken as a callback 
 in tasks, we simply **remove the task with the id from current  
 queue**, **reduce the count to reduce the total number of concurrent tasks**,
-  In the end, we recursively call the run() method to run the 
 current function. Note, here the context remains same. 
- Lastly we bind this function to the current context. 
- We pass this function to task.task function.


Let's now see how this method can be used

Use the following code snippet to see the result. 

```
let runner = new Runner(3);
let task1 = {
  id: 1,
  task: function(done) {
    setTimeout(function() {
      console.log("Task 1");
      done();
    }, 3000)
  }
}

let task2 = {
  id: 2,
  task: function(done) {
    setTimeout(function() {
      console.log("Task 2");
      done();
    }, 5000)
  }
}

let task3 = {
  id: 3,
  task: function(done) {
    setTimeout(function() {
      console.log("Task 3");
      done();
    }, 4000)
  }
}

let task4 = {
  id: 4,
  task: function(done) {
    setTimeout(function() {
      console.log("Task 4");
      done();
    }, 9000)
  }
}

runner.push(task1);
runner.push(task2);
runner.push(task3);
runner.push(task4);
```

**You will see the result on the screen as Task 1, Task 3, Task 2, Task 4**, which is expected.