Promise.all(files.map(upload)) bắn cả 500 request cùng lúc — trình duyệt xếp hàng, backend dễ trả 429, và một lỗi làm hỏng toàn bộ.
Cần một worker pool: tạo N worker, mỗi worker rút việc kế tiếp từ hàng đợi cho tới khi hết.
async function pool(items, limit, worker) {
const results = new Array(items.length)
let cursor = 0
async function run() {
while (cursor < items.length) {
const index = cursor++ // đọc + tăng là đồng bộ nên không có race
results[index] = await worker(items[index], index)
}
}
await Promise.all(Array.from({ length: limit }, run))
return results
}
const results = await pool(files, 5, (file) => upload(file))Điểm mấu chốt: JavaScript đơn luồng nên cursor++ không cần khoá; mỗi worker chỉ nhả quyền điều khiển tại await.
Những phần thường bị hỏi tiếp:
- Lỗi từng phần: bọc worker bằng try/catch (hoặc allSettled bên trong) để một file hỏng không dừng cả pool; giữ lại danh sách lỗi để báo cáo hoặc retry.
- Huỷ: kiểm tra signal.aborted ở đầu mỗi vòng while để dừng sớm.
- Tiến độ: tăng biến đếm sau mỗi item để cập nhật progress bar.
- Nếu backend trả 429 thì kết hợp thêm retry có backoff, hoặc giảm limit động.
Thư viện p-limit / p-map làm đúng việc này; nêu tên chúng vẫn nên kèm cách tự cài như trên.