Đây là giải pháp ES7 của tôi để sao chép-dán thân thiện và có tính năng hoàn chỉnh Promise.all()
/ map()
thay thế, với giới hạn đồng thời.
Tương tự như Promise.all()
nó duy trì thứ tự trả lại cũng như dự phòng cho các giá trị trả về không có hứa hẹn.
Tôi cũng bao gồm một so sánh về việc triển khai khác nhau vì nó minh họa một số khía cạnh mà một số giải pháp khác đã bỏ qua.
Sử dụng
const asyncFn = delay => new Promise(resolve => setTimeout(() => resolve(), delay));
const args = [30, 20, 15, 10];
await asyncPool(args, arg => asyncFn(arg), 4);
Thực hiện
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();
});
}
So sánh
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);
Phần kết luận
asyncPool()
nên là giải pháp tốt nhất vì nó cho phép các yêu cầu mới bắt đầu ngay sau khi bất kỳ yêu cầu nào trước đó kết thúc.
asyncBatch()
được bao gồm dưới dạng so sánh vì việc triển khai nó dễ hiểu hơn, nhưng nó sẽ có hiệu suất chậm hơn vì tất cả các yêu cầu trong cùng một lô bắt buộc phải hoàn thành để bắt đầu lô tiếp theo.
Trong ví dụ giả định này, vani không giới hạn Promise.all()
tất nhiên là nhanh nhất, trong khi những cái khác có thể hoạt động tốt hơn trong một kịch bản tắc nghẽn thế giới thực.
Cập nhật
Thư viện async-pool mà những người khác đã đề xuất có lẽ là một giải pháp thay thế tốt hơn cho việc triển khai của tôi vì nó hoạt động gần như giống hệt nhau và có cách triển khai ngắn gọn hơn với cách sử dụng thông minh Promise.race (): https://github.com/rxaviers/ async-pool / blob / master / lib / es7.js
Hy vọng rằng câu trả lời của tôi vẫn có thể phục vụ một giá trị giáo dục.