Bạn cần tắt trạng thái loading sau một thao tác, nên thêm finally. Sau đó một đồng đội trả về giá trị từ callback và cho rằng nó sẽ trở thành kết quả cuối. Một bài phỏng vấn tốt ở đây không chỉ hỏi dòng console: nó hỏi cách luồng thành công và lỗi đi qua cleanup.
Bài này dùng Promise có sẵn, không request mạng, không timer. Các fixture đã chạy trên Node.js 24.18.1 ngày 11/11/2026 với chế độ báo lỗi unhandled rejection nghiêm ngặt. Không có phép đo thời gian, kiểm thử browser hoặc kết quả từ hệ thống bên ngoài.
1. Giá trị thường từ finally không thay fulfillment gốc
Lưu block sau thành file .mjs, rồi chạy bằng Node. Các callback được viết ngắn để bạn thấy rõ chỗ nào dùng then và chỗ nào dùng finally.
const fromFinally = await Promise.resolve('original')
.finally(() => 'replacement');
const fromThen = await Promise.resolve('original')
.then(() => 'replacement');
console.log(fromFinally, fromThen);
Fixture in original replacement. Nếu cần biến đổi dữ liệu thành công, dùng then hoặc bước tính toán sau await. Callback finally phù hợp hơn cho công việc cần thực hiện khi thao tác đã settled, như kết thúc một trạng thái đang xử lý.
Không nên giải thích kết quả bằng câu “finally không trả gì”. Callback có trả về chuỗi replacement, nhưng hợp đồng của Promise.finally giữ giá trị fulfillment trước đó khi cleanup hoàn tất bình thường. Phân biệt callback trả gì và Promise dẫn xuất kết thúc ra sao giúp tránh trả lời sai ở câu tiếp theo.
2. Cleanup thành công cũng không xóa lỗi gốc
const originalError = new Error('load failed');
try {
await Promise.reject(originalError)
.finally(() => Promise.resolve('cleanup done'));
} catch (error) {
console.log(error === originalError);
}
Fixture in true: lỗi bắt được vẫn là cùng object ban đầu. Việc trả một Promise fulfilled từ cleanup không biến thao tác trước thành thành công. Nếu nghiệp vụ cho phép phục hồi một lỗi và tạo giá trị thay thế, hãy đặt logic đó ở catch với quy tắc rõ ràng.
Ở đây rejection đã được await trong try/catch, nên fixture không cố tình để lỗi không được xử lý. Khi thêm finally vào một Promise thực tế, nhớ rằng phương thức trả về một Promise mới. Chuỗi mà bạn return hoặc await phải bao gồm bước cleanup cần theo dõi.
3. Cleanup thất bại có thể thay lỗi cuối
const loadError = new Error('load failed');
const cleanupError = new Error('cleanup failed');
try {
await Promise.reject(loadError).finally(() => {
throw cleanupError;
});
} catch (error) {
console.log(error === cleanupError);
}
Fixture cũng in true, lần này đối tượng cuối là cleanupError. Nếu cleanup trả Promise rejected, chuỗi cuối cũng đi theo lỗi cleanup. Tài liệu ECMAScript mô tả bước chờ kết quả cleanup trước khi trả lại giá trị hoặc lý do lỗi ban đầu; khi cleanup tự thất bại, đường đi này thay đổi.
Vì vậy, một hàm ghi log có thể ném lỗi không nên được xem là thao tác vô hại trong finally. Quyết định ưu tiên lỗi nào thuộc thiết kế của ứng dụng. Nếu cần giữ lỗi gốc, hãy xử lý lỗi cleanup có chủ đích và ghi nhận được nó; đừng thêm catch rỗng chỉ để bài kiểm thử xanh.
Kiểm tra thứ tự mà không dựa vào đồng hồ
Một fixture bổ sung có thể giữ Promise cleanup ở trạng thái pending bằng cách lưu hàm resolve. Tạo chuỗi finally, gắn observer, nhường một lượt microtask rồi xác nhận observer chưa nhận kết quả. Sau khi gọi resolve cho cleanup và await chuỗi, kết quả gốc mới được chuyển tiếp. Kiểm tra này đã được thực hiện trong lượt biên tập bằng Promise điều khiển thủ công, không đo số mili giây.
Cách kiểm tra trên tách quan hệ phụ thuộc khỏi tốc độ máy. Tránh dùng một timeout rất nhỏ để kết luận cleanup đã được chờ trong mọi môi trường. Bài này không đánh giá vòng đời UI hoặc tác vụ nền sau khi process bị dừng.
Cách trả lời phỏng vấn và lỗi thường gặp
Hãy nêu ba trường hợp: cleanup trả bình thường thì giữ kết quả trước; cleanup trả Promise thành công thì chờ rồi giữ kết quả; cleanup ném lỗi hoặc reject thì chuỗi nhận lỗi mới. Sau đó chỉ ra code nào thực hiện biến đổi dữ liệu và code nào thực hiện cleanup.
Đừng áp dụng máy móc quy tắc của câu lệnh try/finally với return vào Promise.finally. Tên giống nhau không có nghĩa mọi cách chuyển giá trị giống nhau. Các fixture trên giúp bạn giải thích bằng luồng cụ thể thay vì học thuộc một câu ngắn.
Kết luận
Promise.finally thường giữ nguyên kết quả trước đó khi cleanup thành công. Điều cần review kỹ là cleanup bất đồng bộ có được theo dõi và lỗi cleanup có làm che nguyên nhân ban đầu hay không.

