前端面试 P020:定时器延迟、轮询重叠与清理
来源:方格前端讲面试 · 20. setTimeout 和 setInterval 的陷阱,18 分 53 秒。
回看定位:05:22 延迟陷阱 · 07:01 堆积的原分析 · 08:48 递归调度 · 00:53 清理
delay 表示调度条件,不保证准点
setTimeout 安排一次回调,setInterval 安排重复回调。等待条件满足后,回调仍要等宿主调度和当前 JavaScript 执行让出机会。设置 0 不代表同步执行;后台页面、嵌套定时器和系统状态都可能增加延迟。
定时器不是把计算搬到另一个线程。耗时同步回调运行时仍可能阻塞界面,需要拆分任务或使用合适的 Worker;不能靠外面包一层 setTimeout 就声称计算不阻塞。
区分同步占用与异步请求重叠
视频把 setInterval 描绘为固定周期无限堆积同一个同步回调任务,这不是可以对所有浏览器作出的统一保证;实现会有延迟、合并等调度行为。
更直接的实际问题是:interval 不会等待 async 回调的 Promise。上一轮请求还没完成,下个定时回调就可能启动另一个请求,从而重叠并导致响应乱序。
完成后再调度的轮询
function startPolling(task, delay, onError = console.error) {
const controller = new AbortController();
let timer;
async function tick() {
try {
await task(controller.signal);
} catch (error) {
if (!controller.signal.aborted) onError(error);
} finally {
if (!controller.signal.aborted) timer = setTimeout(tick, delay);
}
}
tick();
return () => {
controller.abort();
clearTimeout(timer);
};
}
这里第一轮立即开始,之后按“完成后再等 delay”执行。停止时同时取消后续调度,并通知当前任务中止;任务本身还必须遵守 signal。若任务永久不结束,还需要独立的超时或取消策略。
this 与返回值
浏览器普通定时器回调通常以 Window 为 this,Node.js 通常为 Timeout 对象;不能套用“严格回调一定 undefined”。箭头或 bind 可明确接收者,见 前端面试 P010:普通函数、箭头函数与 this。
浏览器定时器通常返回数字 ID,Node.js 返回对象,不能在跨环境说明中只写“永远返回数字”。清理函数要对应使用并保存引用,组件卸载或业务停止时取消,不让任务继续操作旧页面。
精确显示倒计时应根据截止时间重新计算,而不是每次回调机械减一。动画应考虑 requestAnimationFrame;本集的最小延迟在后续专题继续分析。