Vì forEach bỏ qua giá trị trả về của callback.
Callback async trả về một Promise, forEach vứt đi và chạy tiếp phần tử kế — nên vòng lặp kết thúc ngay lập tức trong khi các save() vẫn đang chạy.
js
async function run(items) {
items.forEach(async (item) => {
await save(item) // Promise bị forEach bỏ qua
})
console.log('done') // in ra trước khi save() nào hoàn tất
}Hai cách sửa, chọn theo yêu cầu:
js
// 1. Tuần tự — cần thứ tự, hoặc sợ quá tải backend
for (const item of items) {
await save(item)
}
// 2. Song song — độc lập nhau, muốn nhanh
await Promise.all(items.map((item) => save(item)))Hai nhược điểm khác của bản forEach: lỗi trong callback không bị try/catch bên ngoài bắt (thành unhandled rejection), và không kiểm soát được số request bắn cùng lúc.
Cùng lý do này, map với callback async cho ra mảng Promise chứ không phải mảng giá trị — luôn cần Promise.all bọc ngoài.