JavaScript - setTimeout 真的准时吗?

288 阅读2分钟

JavaScript - setTimeout 真的准时吗?

本文是作者学习笔记的迁移,因做笔记时未能及时标注内容来源,内容有源自他人网络博客加上自己的思考总结。
如有原作者见此文,可联系我标注引入来源,或者删除内容。


一、setTimeout / setinterval 真的准时嘛?

来段代码:

//同步代码阻塞
let pre = Date.now()
function sleep (wait) {
    let pre = Date.now();
    while (Date.now() - pre < wait) { }
}
sleep(2000) //模拟js代码执行
setTimeout(() => {
    console.log(`${Date.now() - pre} passed`);// 大约3秒后才打印
}, 1000);

因为浏览器的事件循环机制,setTimeout的执行时间是受同步任务执行时长影响的!

引入 requestAnimationFrame

  • 浏览器重绘频率一般会和显示器的刷新率保持同步。大多数浏览器采取 W3C 规范的 建议,浏览器的渲染页面的标准帧率也为 60FPS(frames/ per second)
    • 按帧对网页进行重绘。 该方法告诉浏览器希望执行动画并请求浏览器在下一次重绘之前调用 回调函数来更新动画
    • 由系统来决定回调函数的执行时机在运行时浏览器会自动优化方法的调用
      • 显示器有固定的刷新频率(60Hz 或 75Hz),也就是说,每秒最多只能重绘 60 次或 75 次,requestAnimationFrame 的基本思想让页面重绘的频率与这个刷新频率保持同步

      比如显示器屏幕刷新率为 60Hz,使用requestAnimationFrame API,那么回调函数 就每1000ms / 60 ≈ 16.7ms执行一次;如果显示器屏幕的刷新率为 75Hz,那么回调 函数就每1000ms / 75 ≈ 13.3ms执行一次。

    • 通过requestAnimationFrame调用回调函数引起的页面重绘或回流的时间间隔和显示 器的刷新时间间隔相同。所以 requestAnimationFrame 不需要像setTimeout那样传 递时间间隔,而是浏览器通过系统获取并使用显示器刷新频率 比如一个动画,宽度从 0px 加一递增到 100px。无缓动效果的情况下,浏览器重绘一 次,宽度就加 1。

自定义一个定时器

// 自定义一个定时器
function mySetInterval (callback, interval) {
    let timer
    let startTime = Date.now()//定时器开始计时!
    let endTime
    const loop = () => {
        //这里会根据显示的赫兹来固定计算时间,重复执行loop
        timer = window.requestAnimationFrame(loop)
        endTime = Date.now()
        console.log(endTime - startTime);
        //如果时间到了就执行回调函数
        if (endTime - startTime >= interval) {
            // console.log(endTime, startTime, endTime - startTime);
            startTime = endTime = Date.now()
            // console.log(startTime);
            callback(timer)
        }
    }
    timer = window.requestAnimationFrame(loop)
    return timer
}
let a = 0;
mySetInterval(timer => {
    console.log(1);
    a++;
    if (a === 3) {
        cancelAnimationFrame(timer)
    }
}, 1000)