Giao diện báo hết thời gian chờ, nhưng tác vụ vẫn cập nhật dữ liệu sau đó. Khi dùng Promise.race làm giới hạn chờ, ứng dụng cần quản lý cả kết quả trả về lẫn vòng đời công việc. Bài này dùng ví dụ trong bộ nhớ để nhìn rõ hai trách nhiệm đó trước khi áp dụng vào request thật.
Chạy các khối mã trong async function hoặc môi trường có top-level await. Không có ví dụ nào gọi mạng hoặc ghi dữ liệu production.
1. Kết quả đầu tiên có thể là lỗi
let finish;
const slow = new Promise(resolve => { finish = resolve; });
const first = Promise.race([
slow,
Promise.reject(new Error('unavailable'))
]);
try {
await first;
} catch (error) {
console.log(error.message); // unavailable
}
finish('late success');
console.log(await slow); // late success
race nhận kết quả settled đầu tiên, gồm cả rejection; không phải “thành công đầu tiên”. Đầu vào rỗng khiến kết quả mãi pending. Tham khảo MDN Promise.race.
2. Mô phỏng hết hạn mà không phụ thuộc tốc độ máy
const events = [];
let release;
const gate = new Promise(resolve => { release = resolve; });
const work = gate.then(() => {
events.push('work finished');
return 'result';
});
const deadline = Promise.reject(new Error('deadline exceeded'));
try {
await Promise.race([work, deadline]);
} catch (error) {
events.push(error.message);
}
release();
await work;
console.log(events.join('|')); // deadline exceeded|work finished
gate cho phép test chủ động cho công việc chạy tiếp sau nhánh báo lỗi. Đây là bằng chứng trực tiếp trong ví dụ rằng bên gọi đã ngừng chờ nhưng công việc vẫn còn khả năng tạo tác dụng phụ. Khi áp dụng vào ô tìm kiếm, hãy kiểm tra cả trường hợp kết quả của truy vấn cũ đến sau truy vấn mới.
Không dùng timeout phía client làm bằng chứng một lệnh ghi phía server đã thất bại. Hãy xác định cách tra cứu trạng thái và chính sách chống lặp trước khi tự gửi lại thao tác.
3. Dọn timer ở cả nhánh thành công và lỗi
async function waitAtMost(task, ms) {
if (!Number.isInteger(ms) || ms < 0 || ms > 2147483647) {
throw new RangeError('invalid timeout');
}
let timer;
const deadline = new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('deadline exceeded')), ms);
});
try {
return await Promise.race([task, deadline]);
} finally {
clearTimeout(timer);
}
}
console.log(await waitAtMost(Promise.resolve('ready'), 1000)); // ready
Hàm nhận Promise đã được tạo và chỉ giới hạn lần chờ này. Nó không điều khiển thời điểm công việc bắt đầu. finally dọn timer; xem MDN clearTimeout. return await giữ lần chờ trong khối try để cleanup xảy ra sau kết quả cuộc đua.
Giá trị ms ở đây được giới hạn rõ cho helper minh họa, không phải cam kết thời gian thực. Đừng suy ra công việc sẽ bị ngắt chính xác tại mốc ms, đặc biệt khi event loop đang bận.
4. Hủy cần sự tham gia của tác vụ
const controller = new AbortController();
let release;
const gate = new Promise(resolve => { release = resolve; });
const work = gate.then(() => {
if (controller.signal.aborted) throw controller.signal.reason;
return 'write performed';
});
controller.abort(new Error('cancelled'));
release();
try {
await work;
} catch (error) {
console.log(error.message); // cancelled
}
Trong ví dụ, chính work đọc signal trước bước thực hiện và ném lý do hủy. Với API hỗ trợ hủy, truyền signal theo contract của API; MDN AbortController.abort mô tả khả năng hủy fetch và việc đọc response body. Signal không tự hoàn tác tác dụng phụ đã xảy ra.
Ví dụ gate chỉ minh họa một điểm kiểm tra hợp tác. Nó không dừng mọi phép tính CPU hay đánh thức một Promise pending không hỗ trợ hủy. Tác vụ dài cần thiết kế điểm kiểm tra, cleanup và quyền sở hữu tài nguyên phù hợp.
5. Checklist trước khi dùng timeout
- Bên gọi hết chờ thì công việc còn có thể ghi dữ liệu không?
- Có dọn timer khi task thành công hoặc reject không?
- API hỗ trợ signal và cleanup bằng cách nào?
- Kết quả đến muộn có thể ghi đè trạng thái mới không?
- Test có kiểm tra nhánh hết hạn và tác dụng phụ đến muộn không?
Tám khối mã Việt–Anh đã chạy bằng Node.js, cùng kiểm tra helper với timeout, task reject và timer cleanup. Chưa kiểm thử request mạng, trình duyệt hoặc cơ chế hủy phía server.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.