Ось моє рішення 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()
, звичайно, є найшвидшою, тоді як інші можуть виявитися більш бажаними в реальному сценарії заторів.
Оновлення
Бібліотека асинхронного пулу, яку вже пропонували інші, є, мабуть, кращою альтернативою моїй реалізації, оскільки вона працює майже однаково і має більш стислу реалізацію з розумним використанням Promise.race (): https://github.com/rxaviers/ async-pool / blob / master / lib / es7.js
Сподіваюся, моя відповідь все ще може мати освітню цінність.