Họ đang đo gì
Bạn có nghĩ về đường thất bại không, hay chỉ nghĩ về đường thành công.
Trả lời ngắn~30 giây
all reject ngay khi có một cái hỏng — dùng khi thiếu bất kỳ phần nào thì kết quả vô nghĩa. allSettled luôn resolve và trả trạng thái từng cái — dùng cho dashboard, nơi ba widget chạy được thì hiển thị ba. race lấy kết quả đầu tiên dù thành công hay lỗi — dùng làm timeout. any lấy thành công đầu tiên, bỏ qua lỗi — dùng khi có nhiều nguồn dự phòng.
Giải thích sâu
Điều quan trọng nhất mà cả bốn hàm đều KHÔNG làm: chúng không huỷ những promise còn lại. Promise.all reject không dừng ba request kia — chúng vẫn chạy, vẫn tốn băng thông, và nếu chúng cũng reject thì bạn có unhandled rejection. Muốn huỷ thật thì phải dùng AbortController và truyền signal vào từng fetch.
Cái bẫy thứ hai nằm ở chỗ promise bắt đầu chạy ngay khi được TẠO, không phải khi được await. Nên await Promise.all([a(), b()]) chạy song song, còn viết const x = await a(); const y = await b(); là chạy tuần tự — hai dòng nhìn na ná nhau mà thời gian gấp đôi. Ngược lại, tạo 5.000 promise một lúc rồi Promise.all là cách nhanh nhất để làm sập chính server của bạn; lúc đó cần một pool giới hạn đồng thời.
race có một chi tiết dễ sai khi dùng làm timeout: nếu bạn để setTimeout trong nhánh thua, timer vẫn sống tới khi hết hạn. Với vài nghìn request thì đó là vài nghìn timer treo. Nên AbortSignal.timeout() là lựa chọn tốt hơn khi môi trường hỗ trợ, vì nó huỷ luôn request chứ không chỉ bỏ qua kết quả.
// Dashboard: một widget hỏng không được làm trắng cả trang
const results = await Promise.allSettled([users(), orders(), alerts()]);
const widgets = results.map((r) =>
r.status === 'fulfilled' ? render(r.value) : renderError(r.reason),
);
// Timeout có huỷ thật, không để timer treo
const res = await fetch(url, { signal: AbortSignal.timeout(3000) });
// Tuần tự (chậm gấp đôi) so với song song
const a = await first(); const b = await second(); // 2 × latency
const [a, b] = await Promise.all([first(), second()]); // 1 × latencyCâu hỏi tiếp theo họ sẽ hỏi
?Giới hạn đồng thời ở 10 thì làm sao?
Một pool: giữ tối đa 10 promise đang chạy, mỗi lần một cái xong thì lấy việc tiếp theo. Thư viện như p-limit làm sẵn. Điểm cần nói là VÌ SAO cần: giới hạn kết nối của bên nhận, hoặc pool database của chính bạn.
?Promise.all giữ đúng thứ tự kết quả chứ?
Có — mảng kết quả theo đúng thứ tự mảng đầu vào, không phụ thuộc cái nào xong trước. Đây là điểm khác biệt quan trọng so với việc tự gom kết quả trong for await.
Trả lời thế này là mất điểm
- Cho rằng
Promise.allreject thì các request khác dừng lại. Chúng không dừng, và đây là nguồn unhandled rejection thật. - Dùng
Promise.allcho danh sách không giới hạn kích thước. 10.000 request đồng thời là một cuộc tấn công vào chính mình.