为什么尽量别用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/',
        async: false,
        data: {
            delay: 1
        },
        complete: function () {
        }
    });
    $('#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/

为什么尽量别用 setInterval的更多相关文章

  1. setTimeout和setInterval定时器使用详解测试

    var len=4; while(len--){ var time=setTimeout(function(){ console.log(len); },0); console.log(time); ...

  2. js的setInterval和setTimeout的那些浅坑

    setInterval和setTimeout的区别简单提一下 setInterval() :按照指定的周期(以毫秒计)来调用函数或计算表达式.方法会不停地调用函数,直到 clearInterval() ...

  3. 50 tips of JavaScript

    50 tips of JavaScript,这些坑你都知道吗? 1.在局部作用域中,使用var操作符定义的变量将成为定义该变量的作用域中的局部变量,省略var的会创建全局变量:在全局作用域中,不管是否 ...

  4. Javascript 对象 - 日期对象

    日期对象 在JavaScript中提供了Data对象,用于处理和日期有关的内容.通过Data对象可以获取系统时间.设置时间等.Data对象也具有prototype和constructor属性. 1创建 ...

  5. 50 tips of JavaScript,这些坑你都知道吗?

    1.在局部作用域中,使用var操作符定义的变量将成为定义该变量的作用域中的局部变量,省略var的会创建全局变量:在全局作用域中,不管是否使用var操作符定义的变量都会创建一个全局变量.但是,在全局作用 ...

  6. 你所不知道的setInterval

    在你所不知道的setTimeout记载了下setTimeout相关,此篇则整理了下setInterval:作为拥有广泛应用场景(定时器,轮播图,动画效果,自动滚动等等),而又充满各种不确定性的这set ...

  7. 前端开发:setTimeout与setInterval 定时器与异步循环数组

    前端开发:setTimeout与setInterval 定时器与异步循环数组 前言: 开通博客园三个月以来,随笔记录了工作中遇到的大大小小的难题,也看过无数篇令人启发的文章,我觉得这样的环境是极好的, ...

  8. 关于setInterval()你所不知道的地方

    前言:1.使用setInterval()的定时器会把事件运行的时间也包含在内,如果要精确算定时两个任务之间的时间,可以使用setTimeout()替换.2.当异步事件发生时,如mouse click, ...

  9. Node.js中setTimeout和setInterval的使用

    Node.js和js一样也有计时器,超时计时器.间隔计时器.及时计时器,它们以及process.nextTick(callback)函数来实现事件调度.今天先学下setTimeout和setInter ...

随机推荐

  1. [转贴]CentOS7.5 Kubernetes V1.13(最新版)二进制部署集群

    CentOS7.5 Kubernetes V1.13(最新版)二进制部署集群 http://blog.51cto.com/10880347/2326146   一.概述 kubernetes 1.13 ...

  2. Jquery 临时

    <!--微信小程序--> <div id="page1" class="page page1"> <nav> <div ...

  3. Jenkins配置匿名用户拥有只读权限

    场景:查看cucumber reporting测试报告时需要登陆,比较麻烦 解决:允许匿名用户拥有只读权限 操作:Jenkins->系统管理->全局安全配置->授权策略,勾选“All ...

  4. js手写俄罗斯方块

    代码如下 html: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

  5. 一本通1642【例 2】Fibonacci 第 n 项

    1642: [例 2]Fibonacci 第 n 项 sol:挺模板的吧,经典题吧qaq (1) 1 0    *     1 1     =   1 1 1 0 (2) 1 1    *     1 ...

  6. [代码]--ORA-01843: 无效的月份

    1.插入的日期如果是DateTime类型的,没有影响 2.如果DateTime.ToString()获取的日期,就会报错,例如(@param_datetime = cf.GetServerDateTi ...

  7. 学习4__STM32--中断

    Cortex-M处理器的NVIC接收中断请求各种源 > 从图中可看出,NVIC是一个外设中断的管理器,简化core的工作,控制着整个芯片的中断功能 > NVIC负责给外设中断分配优先级,使 ...

  8. 滥用基于资源约束委派来攻击Active Directory

    0x00 前言 早在2018年3月前,我就开始了一场毫无意义的争论,以证明TrustedToAuthForDelegation属性是无意义的,并且可以在没有该属性的情况下实现“协议转换”.我相信,只要 ...

  9. 利用CSS3实现简书中点击“喜欢”时的动画

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. CSS 设置网页中选中文字的背景色

    在样式文件中增加如下代码: ::selection { background: hsla(5, 92%, 76%, 0.8); color: #fff;}