Введение в управление параллелизмом
В ежедневном процессе разработки вы можете столкнуться с контролем параллелизма сцены, например, с контролем количества одновременных запросов. Так как же управлять параллелизмом в 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
Управление потоком внутри функции. Давайте сначала проанализируемasyncPool
ES7 реализация функции.
Следуйте «Дорога к бессмертному совершенствованию с полным стеком», чтобы прочитать 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.