草庐IT

setTimeout、setInterval、requestAnimationFrame

learninginto 2023-03-28 原文

在异步编程中当然少不了定时器了,常见的定时器函数有setTimeoutsetIntervalrequestAnimationFrame

  • setTimeout

刚开始用setTimeout时,通过会认为设置延时多久,就应该是多久后执行。其实这个观点是错误的,因为JS是单线程执行的,如果前面的代码影响了性能,就会导致setTimeout不会按期执行。这就要求我们不断去修正,使定时器相对准确。

let period = 60 * 1000 * 60 * 2;
let startTime = new Date().getTime();
let count = 0;
let end = new Date().getTime() + period;
let interval = 1000;
let currentInterval = interval;

function loop() {
    count++;
    // 代码执行所消耗的时间
    let offset = new Date().getTime() - (startTime + count * interval)
    let diff = end - new Date().getTime()
    let h = Math.floor(diff / (60 * 1000 * 60))
    let hdiff = diff % (60 * 1000 * 60)
    let m = Math.floor(hdiff / (60 * 1000))
    let mdiff = hdiff % (60 * 1000)
    let s = mdiff / 1000
    let sCeil = Math.ceil(s)
    let sFloor = Math.floor(s)
    //得到下一次循环所消耗的时间
    currentInterval = interval - offset;
    console.log(`时:${h},分:${m},秒:${s},代码执行用时${offset}ms,下次循环间隔${currentInterval}`)
    setTimeout(loop, currentInterval)
}
setTimeout(loop, currentInterval)
  • setInterval

通常来说不建议使用setInterval。第一,它和setTimeout一样,不能保证在预期的时间执行任务;第二,它存在执行累积的问题。如果定时器执行过程中出现了耗时操作,多个回调函数会在耗时操作结束后同时执行,从而带来性能上的问题。

如果有循环定时器的请求,完全可以通过requestAnimationFrame来实现。

function setInterval(callback, interval) {
    let timer;
    const now = Date.now;
    let startTime = now();
    let endTime = startTime;
    const loop = () => {
        timer = window.requestAnimationFrame(loop)
        endTime = now()
        if (endTime - startTime >= interval) {
            startTime = endTime = now()
            callback(timer)
        }
    }
    timer = window.requestAnimationFrame(loop)
    return timer
}

let a = 0
setInterval(timer => {
    console.log('1', 1)
    a++
    if (a === 3) {
        cancelAnimationFrame(timer)
    }
}, 1000)
  • requestAnimationFrame

计时器一直是javascript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化

大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms

而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行

requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果

requestAnimationFrame自带函数节流功能,基本可以保证在16.6毫秒内只执行一次(不掉帧的情况下),并且该函数的延时效果是精确的,没有其他定时器时间不准的问题,同样可以通过该函数实现setTime

  • 总结
  1. requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量

  3. requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。

有关setTimeout、setInterval、requestAnimationFrame的更多相关文章

  1. ruby - setInterval() 相当于 ruby - 2

    在JavaScript中你可以这样做:setInterval(func,delay);我似乎无法在谷歌上找到任何我真正要找的东西。是否有ruby等价物?提前致谢。 最佳答案 你可以做类似的事情:Thread.newdoloopdosleepdelay#yourcodehereendend或者你可以定义一个函数:#@return[Thread]returnloopthreadreferencedefset_interval(delay)Thread.newdoloopdosleepdelayyield#callpassedblocke

  2. javascript - 在 JavaScript 中使用 setTimeout() 和 setInterval() 时调用函数 - 2

    这个问题在这里已经有了答案:Whatisthedifferencebetweenafunctioncallandfunctionreference?(6个答案)关闭1年前。如果我使用不带括号的setTimeout()和setInterval()调用命名函数,它会按预期工作。当我用括号调用同一个函数时,它要么立即执行要么给出错误。与我在网上找到的内容相比,我正在寻找对此事更深入的了解。你们能给我解释一下为什么这是真的吗?varfunc=function(){console.log("Bowtiesarecool.");}setTimeout(func(),1500);//Prints"B

  3. javascript - 如何为 setInterval() 函数编写测试用例 - 2

    我有一个计时器并假设当计数器计数到3时将执行一个特定的函数。vara_interval_function=function(){varcounter=1;varinterval=setInterval(function(){if(counter===5){clearInterval(interval);}//runthefunctionwhenthecounteris3if(counter===3){a_function_should_be_runned();}counter++;},500);returninterval;}但是,我不知道如何建立一个有效的测试用例来测试计数器以及函数

  4. javascript - 使用核心javascript时ajax调用中的settimeout - 2

    我有一个调用ajax的JavaScript函数。现在我需要在此函数中添加超时,就像调用服务花费的时间超过污染时间时,ajax调用应该超时并显示默认消息。我不想在其中使用Jquery。这是我的代码:AJAX=function(url,callback,params){vardt=newDate();url=(url.indexOf('?')==-1)?url+'?_'+dt.getTime():url+'&_'+dt.getTime();if(url.indexOf('callback=')==-1){ajaxCallBack(url,function(){if(this.readySt

  5. javascript - 使用 javascript setInterval 会增加 CPU 的内存消耗 - 2

    我正在开发一个.NETWeb应用程序,我必须使用javascript函数setInterval()执行ajax请求以刷新某些页面的信息。对于每个ajax请求,我都会收到大约68KB的xml响应,我设法通过jQuery在html中进行视觉更改。我将间隔设置为2000毫秒,但我想,或者更确切地说,我需要将它减少到1000毫秒。不幸的是,每次请求都会增加CPU的内存消耗,这会导致浏览器被阻塞,除非重新加载页面,否则用户无法使用它。我在Firefox、InternetExplorer和Chrome中对此进行了测试,但结果始终相同。如果我不执行setInvertal(),问题就会消失。此外,我一

  6. javascript - 如何每次使用随机毫秒数的setInterval? - 2

    函数showRandom每1000毫秒执行一次,但我希望它每random毫秒执行一次。有什么解决方案吗?谢谢!varrandom=1000;setInterval(function(){random=randomizator(60000,200000);},1000);setInterval(function(){showRandom(random);},random);functionrandomizator(a,b){returnMath.floor(Math.random()*b)+a;}functionshowRandom(random){$('#test').text(ran

  7. javascript - 为什么 setInterval() 周期每次都变快? - 2

    我正在Javascript上构建自定义slider,我希望每次用户单击slider的div时,slider都应停止X秒。我的代码是:$(document).ready(function(){varciclo;varindex_slide=1;functionstartSlidercicle(){ciclo=setInterval(function(){//Slidercodegoeshere},3000);}//HereIstarttheslideranimationstartSlidercicle();//Whentheuserclicksonadivcalled'slide',st

  8. javascript - 为什么 setInterval() 会忽略错误? - 2

    Javascript的setInterval()似乎不关心它调用的代码是否抛出异常。例如,这不会终止程序,而是一遍又一遍地调用该函数:setInterval(function(){throw"error"},1000);这种行为的原因是什么?它记录在任何地方吗? 最佳答案 throw的MDN文档比如,对于抛出的物体:Ifnocatchblockexistsamongcallerfunctions,theprogramwillterminate.这并不完全准确。ECMAScriptspecsection10.4Athrownexcep

  9. javascript - 使用 setInterval 调用时,对象方法失去其作用域 - 2

    有没有办法像下面的例子一样打印出数组players的值?几个小时以来,我一直在努力寻找解决方案...functionRoom(name,id,owner){this.players=[];this.movementz=function(){console.log(this.players);}}我正在使用setInterval调用该函数,如下所示:setInterval(room.movementz,1000); 最佳答案 这里的问题是关于this对象:创建你的对象并手动调用它的movementz方法会起作用,因为this元素是对象

  10. javascript - 为什么 setTimeout(.., 0) 不立即执行? - 2

    vartimeout=setTimeout(function(){console.log("I'mmessagefromtimeout");},0);console.log("I'mmessagefromoutsidetimeout");//1.I'mmessagefromoutsidetimeout//2.I'mmessagefromtimeout为什么内部指令不首先执行,尽管将setTimeout时间设置为0?我使用了各种时间,包括0/null,我想知道如何保留setTimeout对象并随流程执行其指令。 最佳答案 Javasc

随机推荐