Về giá trị cuối thì giống nhau — cả hai đều cho ra cùng kết quả cho người gọi.
Khác biệt nằm ở try/catch và stack trace.
async function bad() {
try {
return fetchUser() // catch KHÔNG bắt được lỗi
} catch (err) {
return fallback()
}
}
async function good() {
try {
return await fetchUser() // catch bắt được
} catch (err) {
return fallback()
}
}Với return promise, hàm thoát khỏi khối try ngay lập tức rồi mới trả promise ra ngoài; lúc promise reject thì catch đã không còn hiệu lực. return await giữ hàm dừng lại bên trong try cho tới khi promise settle, nên lỗi rơi đúng vào catch. Điều tương tự áp dụng với finally — bản không await chạy finally trước khi công việc thật xong.
Bắt buộc return await khi: ở trong try/catch/try/finally, hoặc muốn stack trace giữ được khung hàm hiện tại khi debug lỗi async.
Ngoài try thì return await chỉ thêm một microtask thừa — vô hại về mặt logic, và rule no-return-await của ESLint đã được nới lại (no-return-await bị deprecate, thay bằng no-extra-await-kiểu tuỳ chọn) chính vì ngoại lệ try/catch này.