Вот мое решение ES7 для копирования и вставки с полной Promise.all()
/ map()
альтернативной функцией с ограничением параллелизма.
Подобно тому, как Promise.all()
он поддерживает порядок возврата, а также резерв для возвращаемых значений, не обещающих.
Я также включил сравнение различных реализаций, поскольку оно иллюстрирует некоторые аспекты, которые упустили некоторые другие решения.
Применение
const asyncFn = delay => new Promise(resolve => setTimeout(() => resolve(), delay));
const args = [30, 20, 15, 10];
await asyncPool(args, arg => asyncFn(arg), 4);
Реализация
async function asyncBatch(args, fn, limit = 8) {
args = [...args];
const outs = [];
while (args.length) {
const batch = args.splice(0, limit);
const out = await Promise.all(batch.map(fn));
outs.push(...out);
}
return outs;
}
async function asyncPool(args, fn, limit = 8) {
return new Promise((resolve) => {
const argQueue = [...args].reverse();
let count = 0;
const outs = [];
const pollNext = () => {
if (argQueue.length === 0 && count === 0) {
resolve(outs);
} else {
while (count < limit && argQueue.length) {
const index = args.length - argQueue.length;
const arg = argQueue.pop();
count += 1;
const out = fn(arg);
const processOut = (out, index) => {
outs[index] = out;
count -= 1;
pollNext();
};
if (typeof out === 'object' && out.then) {
out.then(out => processOut(out, index));
} else {
processOut(out, index);
}
}
}
};
pollNext();
});
}
Сравнение
const asyncFn = delay => new Promise(resolve => setTimeout(() => {
console.log(delay);
resolve(delay);
}, delay));
const args = [30, 20, 15, 10];
const out1 = await Promise.all(args.map(arg => asyncFn(arg)));
const out2 = await asyncPool(args, arg => asyncFn(arg), 2);
const out3 = await asyncBatch(args, arg => asyncFn(arg), 2);
console.log(out1, out2, out3);
Вывод
asyncPool()
должен быть лучшим решением, поскольку он позволяет запускать новые запросы сразу после завершения любого предыдущего.
asyncBatch()
включен для сравнения, так как его реализация проще для понимания, но должна быть медленнее по производительности, так как все запросы в одном пакете должны завершиться, чтобы начать следующий пакет.
В этом надуманном примере ваниль без ограничений, Promise.all()
конечно, самый быстрый, в то время как другие могут работать более желательно в сценарии перегрузки в реальном мире.
Обновить
Библиотека async-pool, которую уже предлагали другие, вероятно, является лучшей альтернативой моей реализации, поскольку она работает почти идентично и имеет более краткую реализацию с умным использованием Promise.race (): https://github.com/rxaviers/ асинхронный пул / blob / master / lib / es7.js
Надеюсь, мой ответ по-прежнему будет иметь образовательную ценность.