function setTimeoutRAF(callback, delay) {
const startTime = performance.now();
let animationFrameId = null;
function loop(currentTime) {
const elapsed = currentTime - startTime;
if (elapsed >= delay) {
callback();
} else {
animationFrameId = requestAnimationFrame(loop);
}
}
animationFrameId = requestAnimationFrame(loop);
// 返回一个清除函数
return function clearTimeoutRAF() {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
};
}
// 使用示例
const clearTimer = setTimeoutRAF(() => {
console.log('延迟 2000ms 执行');
}, 2000);
// 取消定时器
// clearTimer();
注意
实际延迟时间可能会有微小偏差(± 几毫秒)
在后台标签页中,requestAnimationFrame 会暂停执行
对于精确时间要求不高的场景,原生 setTimeout 可能更合适
在处理需要大于 1 帧时长(60 帧=1/60s)的任务时会造成阻塞
优势对比
-
动画效果
可能丢帧、卡顿
更平滑,与刷新率同步
-
后台标签页
继续执行(可能累积)
自动暂停,节省资源
-
电池寿命
可能更耗电
浏览器优化,更省电
-
60fps 需求
时间不精确
天然适配 60fps
应用场景
适用涉及动画、视觉变化、布局更新的场景
- 批量 DOM 操作:将多个 DOM 更新延迟到同一帧
setTimeoutRAF(() => {
// 批量执行DOM修改,减少重排
element1.style.width = '100px';
element2.style.height = '200px';
}, 100);
- 懒加载和分片加载
// 分片渲染大数据
function renderChunkedData(data, chunkSize = 100) {
let index = 0;
function renderNextChunk() {
const chunk = data.slice(index, index + chunkSize);
renderToDOM(chunk);
index += chunkSize;
if (index < data.length) {
setTimeoutRAF(renderNextChunk, 0); // 下一帧继续
}
}
function renderToDOM(chunk) {
//DocumentFragment文档片段插入
}
renderNextChunk();
}
评论区