Как реализовать контроль параллелизма в JavaScript?

внешний интерфейс JavaScript
Как реализовать контроль параллелизма в JavaScript?

Введение в управление параллелизмом

В ежедневном процессе разработки вы можете столкнуться с контролем параллелизма сцены, например, с контролем количества одновременных запросов. Так как же управлять параллелизмом в JavaScript? Прежде чем ответить на этот вопрос, мы кратко объясним управление параллелизмом.

Предположим, есть шесть задач, которые нужно выполнить, и мы хотим ограничить количество задач, выполняемых одновременно, а именно одновременно можно выполнять не более двух задач. когдаСписок задач в процессеПосле любого 1 задания вСписок делчтобы получить новую задачу и добавить задачу вСписок задач в процессесередина. Для того, чтобы каждый мог более интуитивно понять описанный выше процесс, Apogo нарисовал следующие 3 картинки:

1.1 Фаза первая

1.2 Второй этап

1.3 Третий этап

Хорошо, после введения контроля параллелизма Брат Абао опубликует его на Github.async-poolЭта библиотека представляет конкретную реализацию управления параллельным выполнением асинхронных задач.

async-pool:GitHub.com/R Xavier IS/Аспен…

Запуск нескольких функций с возвратом обещаний и асинхронных функций БЕЗ СОВМЕСТНОГО КОНКУРЕНТА С ИСПОЛЬЗОВАНИЕМ НАТИВНЫХ ES6 / ES7.

противasync-poolКонкретное применение этой библиотеки брат Абао написалКак реализовать одновременную загрузку больших файлов в JavaScript?а такжеКак реализовать одновременную загрузку больших файлов в JavaScript?Две статьи, заинтересованные друзья могут узнать об этом.

Во-вторых, реализация контроля параллелизма

async-poolЭта библиотека предоставляет реализации в двух разных версиях, ES7 и ES6. Прежде чем анализировать конкретную реализацию, давайте посмотрим, как она используется.

2.1 Использование асинхронного пула

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);

В приведенном выше коде мы используемasync-poolпредоставляется этой библиотекойasyncPoolфункция для реализации параллельного управления асинхронными задачами.asyncPoolСигнатура функции выглядит так:

function asyncPool(poolLimit, array, iteratorFn){ ... }

Эта функция принимает три параметра:

  • poolLimit(числовой тип): указывает количество одновременных ограничений;
  • array(Тип массива): представляет массив задач;
  • iteratorFn(Тип функции): представляет итеративную функцию, которая используется для обработки каждого элемента задачи и возвращает объект Promise или асинхронную функцию.

Для приведенного выше примера, используяasyncPoolПосле функции соответствующий процесс выполнения выглядит следующим образом:

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
// Call iterator (i = 1000)
// Call iterator (i = 5000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 1000 finishes
// Call iterator (i = 3000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 3000 finishes
// Call iterator (i = 2000)
// Itaration is complete, wait until running ones complete...
// 5000 finishes
// 2000 finishes
// Resolves, results are passed in given array order `[1000, 5000, 3000, 2000]`.

Наблюдая за приведенной выше аннотационной информацией, мы можем примерно понятьasyncPoolУправление потоком внутри функции. Давайте сначала проанализируемasyncPoolES7 реализация функции.

Следуйте «Дорога к бессмертному совершенствованию с полным стеком», чтобы прочитать 4 бесплатные электронные книги (всего более 30 000 загрузок) и 50 серий руководств по TS, изначально написанных А Баогэ.

2.2 Реализация асинхронного пула ES7

async function asyncPool(poolLimit, array, iteratorFn) {
  const ret = []; // 存储所有的异步任务
  const executing = []; // 存储正在执行的异步任务
  for (const item of array) {
    // 调用iteratorFn函数创建异步任务
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p); // 保存新的异步任务

    // 当poolLimit值小于或等于总任务个数时,进行并发控制
    if (poolLimit <= array.length) {
      // 当任务完成后,从正在执行的任务数组中移除已完成的任务
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e); // 保存正在执行的异步任务
      if (executing.length >= poolLimit) {
        await Promise.race(executing); // 等待较快的任务执行完成
      }
    }
  }
  return Promise.all(ret);
}

В приведенном выше коде полное использованиеPromise.allа такжеPromise.raceОсобенности функций в сочетании с ES7async awaitОсобенности, со временем реализована функция параллельного управления. использоватьawait Promise.race(executing);Эта строка заявления, мы будем ждатьСписок задач в процессеСледующий цикл не будет продолжаться до тех пор, пока не будет завершена более быстрая задача в процессе.

Реализация asyncPool ES7 относительно проста.async awaitКак функция достигает той же функциональности.

2.3 Реализация асинхронного пула ES6

function asyncPool(poolLimit, array, iteratorFn) {
  let i = 0;
  const ret = []; // 存储所有的异步任务
  const executing = []; // 存储正在执行的异步任务
  const enqueue = function () {
    if (i === array.length) {
      return Promise.resolve();
    }
    const item = array[i++]; // 获取新的任务项
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    let r = Promise.resolve();

    // 当poolLimit值小于或等于总任务个数时,进行并发控制
    if (poolLimit <= array.length) {
      // 当任务完成后,从正在执行的任务数组中移除已完成的任务
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        r = Promise.race(executing); 
      }
    }
 
    // 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务
    return r.then(() => enqueue());
  };
  return enqueue().then(() => Promise.all(ret));
}

В версии реализации ES6 через внутренний пакетenqueueфункция для реализации основной логики управления. когдаPromise.race(executing)вернутьPromiseВызывается только тогда, когда объект становится завершеннымenqueueфункция, отarrayПолучайте новые задачи из массива.

3. Брату А Бао есть что сказать

существуетasyncPoolВ конкретной реализации ES7 и ES6 этой библиотеки мы ее использовали.Promise.allа такжеPromise.raceфункция. из которых почеркPromise.allЭто распространенный вопрос на собеседовании. Воспользовавшись этой возможностью, брат А Бао и все собрались вместе, чтобы написать упрощенную версиюPromise.allа такжеPromise.raceфункция.

3.1 Рукописное обещание.все

Promise.all(iterable)Метод вернет промис-объект, когда состояние всех входных промисов станетresolved, возвращаемый объект обещания вернет результат после разрешения каждого объекта обещания в виде массива. Когда состояние любого из входных объектов-обещаний становитсяrejected, возвращенный объект обещания отклонит соответствующее сообщение об ошибке.

Promise.all = function (iterators) {
  return new Promise((resolve, reject) => {
    if (!iterators || iterators.length === 0) {
      resolve([]);
    } else {
      let count = 0; // 计数器,用于判断所有任务是否执行完成
      let result = []; // 结果数组
      for (let i = 0; i < iterators.length; i++) {
        // 考虑到iterators[i]可能是普通对象,则统一包装为Promise对象
        Promise.resolve(iterators[i]).then(
          (data) => {
            result[i] = data; // 按顺序保存对应的结果
            // 当所有任务都执行完成后,再统一返回结果
            if (++count === iterators.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err); // 任何一个Promise对象执行失败,则调用reject()方法
            return;
          }
        );
      }
    }
  });
};

Следует отметить, что дляPromise.allСтандартная реализация , его параметр — это итерируемый объект, такой как Array, String или Set и т. д.

3.2 Рукописное обещание.race

Promise.race(iterable)Метод возвращает объект обещания, как только объект обещания в итератореresolvedилиrejected, возвращенный объект обещания разрешит или отклонит соответствующее значение.

Promise.race = function (iterators) {
  return new Promise((resolve, reject) => {
    for (const iter of iterators) {
      Promise.resolve(iter)
        .then((res) => {
          resolve(res);
        })
        .catch((e) => {
          reject(e);
        });
    }
  });
};

В этой статье брат Абао проводит подробный анализasync-poolКонкретная реализация асинхронного контроля параллелизма задач, а также для того, чтобы каждый мог лучше понятьasync-poolосновной код. В конце концов, брат А Бао также заставил всех вместе написать упрощенную версию от руки.Promise.allа такжеPromise.raceфункция. На самом деле, кромеPromise.allКроме функции есть еще одна функция -Promise.allSettled, который используется для решенияPromise.allПроблемы, интересующие маленького партнера, можно посмотреть самостоятельно.

Следуйте «Дорога полного стека», чтобы прочитать 4 бесплатные электронные книги (всего более 30 000 загрузок) и 11 руководств по Vue 3 для продвинутых пользователей.Друзья, которые хотят вместе изучать TS/Vue 3.0, могут добавить Abaoge WeChat —— semlinker.

4. Справочные ресурсы