为什么尽量别用 setInterval

摘自公众号前端大全---仅做个人学习之用!

英文: Colin Snover  译文:众成翻译/为之漫笔

www.zcfy.cc/article/why-i-consider-setinterval-to-be-harmful-zetafleet


为什么尽量别用setInterval


在开发一个在线聊天工具时,经常会有过多少毫秒就重复执行一次某操作的需求。“没问题”,大家都说,“用setInterval好了。”我觉得这个点子很糟糕。

原因之一:setInterval无视代码错误


setInterval有个讨厌的习惯,即对自己调用的代码是否报错这件事漠不关心。换句话说,如果setInterval执行的代码由于某种原因出了错,它还会持续不断(不管不顾)地调用该代码。看代码

function a() {
    try{
        a.error.here;

    } catch(e){
        $('body').append('<div>' + e.toString() + '</div>');
        throw e;
    }

}

function b() {
    try{
        b.error.here;

    } catch(e)
    {
        $('body').append('<div>' + e.toString() + '</div>');
        throw e;
    }
    setTimeout(b, 2000);
}

setInterval(a, 2000);
setTimeout(b, 2000);


原因之二:setInterval无视网络延迟


假设你每隔一段时间就通过Ajax轮询一次服务器,看看有没有新数据(注意:如果你真的这么做了,那恐怕你做错了;建议使用“补偿性轮询”(backoff polling)[1])。而由于某些原因(服务器过载、临时断网、流量剧增、用户带宽受限,等等),你的请求要花的时间远比你想象的要长。但setInterval不在乎。它仍然会按定时持续不断地触发请求,最终你的客户端网络队列会塞满Ajax调用。看代码

var n = 0,
    t = 0,
    u = 0,
    i, s = 'Stopping after 25 requests, to avoid killing jsfiddle’s server';

function a() {
    $.post('/ajax_html_echo/'function () {
        --n;
    });
    ++n;
    ++t;
    $('#reqs').html(n + ' a() requests in progress!');
    if (t > 25) {
        clearInterval(i);
        $('#reqs').html(s);
    }
}

function b() {
    ++u;
    $.post('/ajax_html_echo/'function () {
        $('#req2').html('b(): ' + new Date().toString());
        if (u <= 25) {
            setTimeout(b, 500);
        } else {
            $('#req2').html(s);
        }
    });
}

i = setInterval(a, 500);
setTimeout(b, 500);


原因之三:setInterval不保证执行


与setTimeout不同,你并不能保证到了时间间隔,代码就准能执行。如果你调用的函数需要花很长时间才能完成,那某些调用会被直接忽略。看代码

function slow() {
    $.ajax({
        url'/echo/html/',
        asyncfalse,
        data: {
            delay: 1
        },
        completefunction () {
        }
    });
    $('#reqs').text(~~((new Date() - start) / 100) + ' expected, ' + iters + ' actual');
    if (iters++ > 4) {
        $('#reqs').append('<br>Stopping after 5 iterations');
        clearInterval(iv);
    }
};
var iv = setInterval(slow, 100), start = +new Date(), iters = 0;


解决之道很简单:用setTimeout


与其使用setInterval,不如在适当的时刻通过setTimeout来调用函数自身。在前面两个示例中,使用setInterval的函数a都出错了,而使用setTimeout的函数b则表现很好。

如果必须保证间隔相等怎么办?


如果确实要保证事件“匀速”被触发,那可以用希望的延迟减去上次调用所花时间,然后将得到的差值作为延迟动态指定给setTimeout。 不过,要注意的是JavaScript的计时器并不是非常精确[2]。因此你不可能得到绝对“平均”的延迟,即使使用setInterval也不行,原因很多(比如垃圾回收、JavaScript是单线程的,等等)。此外,当前浏览器也会将最小的超时时间固定在4ms到15ms之间。因此不要指望一点误差也没有。

文中链接

1.http://github.com/blog/467-smart-js-polling

2.http://ejohn.org/blog/accuracy-of-javascript-time/

JavaScript中,`setInterval` 是一个用于定期执行指定函数的内置方法。然而,如果使用不当或处理不当,它可能会导致页面卡顿,也就是我们常说的性能问题。以下是可能导致页面卡顿的一些原因: 1. 频繁的DOM操作:`setInterval` 中的函数如果频繁地修改DOM树,比如大量元素的添加、删除或样式更改,这会占用大量的浏览器渲染资源,使页面响应变慢。 2. 无限循环或未及时清除:如果定时器内的函数没有正确的结束条件或者没有被正确地清除(例如忘记调用 `clearInterval`),它将持续运行,直到页面关闭,这会导致CPU负载过高。 3. CPU密集型计算:如果定时器内的函数执行了复杂的运算或I/O操作,这些任务可能占用大部分处理器时间,使得页面暂停响应。 4. 错误的事件监听:如果定时器关联的事件处理函数引起了事件冒泡或阻止默认行为,而这些行为需要大量处理,也可能导致页面卡顿。 为避免这些问题,你可以采取以下措施优化: - 尽量减少DOM操作次数,尤其是当操作涉及到大量元素时。 - 在定时器内完成工作后,确保清除定时器,避免不必要的资源占用。 - 如果有循环,考虑使用 `requestAnimationFrame` 替代,因为它的执行时机更符合浏览器的渲染流程。 - 对于复杂的任务,考虑使用 Web Worker 来在后台线程中运行,减轻主线程负担。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值