Skip to content

Iterate Asynchronously in Parallel with Concurrency Limit

js
const tasks = []; // array of async tasks
let concurrency = 2,
  running = 0,
  completed = 0,
  index = 0;

function next() {
  while (running <= concurrency && index < tasks.length) {
    task = tasks[index++];
    task(function () {
      // async callback: will execute when async task completes

      // when all tasks are completed, exit function
      if (completed === tasks.length) {
        return finish();
      }

      // when there are still tasks remaining in array
      completed++, running--;
      next(); // trigger while loop once again
    });
    running++;
  }
}

next();

function finish() {
  // all tasks completed
}

A function with a pattern above works nicely until the same function is called many times, where there could be more than 2 concurrent tasks running at once. What we need in this case is a ‘global’ concurrency limit, where concurrency limits & counts are kept in a central entity. This can be achieved with a TaskQueue class implementation:

js
class TaskQueue {
  constructor(concurrency) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }

  pushTask(task) {
    this.queue.push(task);
    this.next();
  }

  next() {
    while (this.running <= this.concurrency && this.queue.length) {
      const task = this.queue.shift();
      task(function () {
        this.running--;
        this.next();
      });
      this.running++;
    }
  }
}

const downloadTask = new TaskQueue(2);
downloadTask.pushTask(task1);
downloadTask.pushTask(task2);