DevGang
Авторизоваться

Создание асинхронного исполнителя задач с параллелизмом в JavaScript

Давайте представим, что нам нужно создать асинхронный обработчик задач в javascript со следующим ограничением:

  1. У исполнителя задач должен быть набор задач (task), которые будут ему переданы.
  2. Каждая из этих задач будет некоторой асинхронной операцией,
  3. Исполнитель задач также должен убедиться, что в один момент времени может выполняться только заданное количество задач, а другие задачи продолжают ждать, пока не придет их очередь.

Давайте сначала закодируем решение

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;
      }
}

Давайте разберем этот фрагмент кода построчно:

  1. Сначала мы передаем флаг concurrency, указывающий, сколько задач (task) может выполняться одновременно. waitList используется для создания списка ожидания задач, которые будут ожидать, пока очередь, называемая currentQueue, не станет пустой. Переменная count инициализируется для подсчета количества одновременных задач в данный момент.
  2. Метод push используется для отправки задачи в waitList, а затем мы выполняем метод запуска. Это просто указывает на то, что задачи необходимо запускать, если очередь пуста.
  3. Интересен метод запуска. Во-первых, он сохраняет контекст задачи благодаря первой строке, обозначенной как let current = this
  4. Затем мы проверяем параллелизм, и если в waitList есть элементы, мы просто:
  • поместим идентификатор задачи в currentQueue,
  • отображаем текущие запущенные задачи с помощью метода showRunningTasks,
  • в глубине метода done, который будет использоваться в качестве обратного вызова в задачах, мы просто удаляем задачу с идентификатором из текущей очереди, уменьшаем количество, чтобы уменьшить общее количество одновременных задач,
  • В конце мы рекурсивно вызываем метод run() для запуска текущей функции. Обратите внимание, здесь контекст остается прежним.
  • Наконец, мы привязываем эту функцию к текущему контексту.
  • Мы передаем эту функцию в task.task function.

Давайте теперь посмотрим, как можно использовать этот метод.

Используйте следующий фрагмент кода, чтобы увидеть результат.

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);

Вы увидите результат на экране как Task 1, Task 3, Task 2, Task 4, что и ожидается.

#JavaScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться