【问题标题】:Javascript setTimeout unexpected outputJavascript setTimeout 意外输出
【发布时间】:2015-09-25 19:59:26
【问题描述】:

我一直在使用在我的应用程序中使用 setInterval(fn,delay) 函数的脚本,在阅读了 setTimeout 和 JS 的工作原理之后,我遇到了一些奇怪的结果,所以我做了一个测试: 这是jsfiddle https://jsfiddle.net/jyq46uu1/2/

建议的代码:

var limit        = 1000000000;
var intervals    = 0;
var totalTime    = new Date();
var startTime    = new Date();

var uid = setInterval(
    function () {
        // final lap?
        if (intervals == 9) clearInterval(uid);

        for (i = 0; i < limit; i += 1) {
            // just working the CPU
        }
        // reduce the iterations
        limit = limit / 10;   
        intervals += 1;        

        console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
        // reset the time
        startTime    = new Date();
    }, 250, 9);

好的,根据我从http://ejohn.org/blog/how-javascript-timers-work/ Javascript 得到的消息,即使“线程被阻塞”,也会在 setInterval 中为函数调用计时器,所以如果函数仍在执行,则调用只是排队等等依此类推......在我的笔记本中,代码会产生这样的结果:

"Interval 1 Time elapsed    : 4264" 
"Interval 2 Time elapsed    : 477" 
"Interval 3 Time elapsed    : 91" 
"Interval 4 Time elapsed    : 170" 
"Interval 5 Time elapsed    : 246" 
"Interval 6 Time elapsed    : 242" 
"Interval 7 Time elapsed    : 248" 
"Interval 8 Time elapsed    : 248" 
"Interval 9 Time elapsed    : 248"

好的,如果我的红色是真的,到第一个间隔结束时,所有函数调用都在队列中......在我的脚本中,我正在减少每次执行的工作量,因此每次调用应该花费更少秒比前一个,但无论我设置多少迭代,经过的时间总是在第 4 次运行后加快间隔速度。 也许我弄错了,但如果到 4264 时所有函数都已经在队列中并且假设立即运行,它们应该显示更少的时间,对吗? ...如果第 3 次迭代显示 91 并且其他的就在后面,他们应该取 91 或更少。但事实并非如此。

如果你明白发生了什么,请向我解释一下,因为我认为我遗漏了一些东西。

【问题讨论】:

    标签: javascript timer settimeout


    【解决方案1】:

    我不确定我是否正确,这取决于我对“Javascript”的了解。

    Javascript 是基于基于事件的触发,并且“没有队列和等待”。它将只是同时进行(类似于线程)。

    我想这将是有意义的,因为它解释和占用下一个。

    【讨论】:

    • 根本不是@Alexander,请在此处阅读。 stackoverflow.com/questions/2734025/…
    • 阅读该答案中评分最高的 cmets。这种行为不会使 JavaScript 成为多线程的。这是脚本调用环境 API 时主机的行为,它可以在返回调用 API 的代码之前调用更多代码。您的代码的任何两个部分都不会同时执行(没有工作人员)。
    • 相当有争议,当您能够同时执行/调用多个脚本时。不是叫多线程吗?
    【解决方案2】:

    我认为第一次不是从队列中的初始间隔开始计算,而是从你设置 startTime 的点开始计算。

    那么第一个定时器就是统计初始化时间、环境创建、变量和定时器分配。

    试试这个修改:

    var limit        = 1000000000;
    var intervals    = 0;
    var totalTime    = new Date();
    var startTime    = false;
    
    var uid = setInterval(
        function () {
            if (!startTime) startTime = new Date();
            // final lap?
            if (intervals == 9) clearInterval(uid);
    
            for (i = 0; i < limit; i += 1) {
                // just working the CPU
            }
            // reduce the iterations
            limit = limit / 10;   
            intervals += 1;        
    
            console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
            // reset the time
            startTime    = new Date();
        }, 250, 9);
    

    此外,第一次需要更长的时间可能是因为函数正在被解释,然后后续时间正在执行“编译”/由 Javascript JIT 优化(请参阅https://en.wikipedia.org/wiki/Just-in-time_compilation)。查看此代码以查看证明

    var limit        = 1000000000;
    var intervals    = 0;
    var totalTime    = new Date();
    var startTime    = new Date();
    
    var uid = setInterval(
        function () {
            startTime = new Date();
            // final lap?
            if (intervals == 9) clearInterval(uid);
    
            for (i = 0; i < limit; i += 1) {
                // just working the CPU
            }
            // reduce the iterations
            limit = limit / 10;   
            intervals += 1;        
    
            console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
            // reset the time
            startTime    = new Date();
        }, 250, 9);
    

    输出:

    Interval 1 Time elapsed : 3249
    Interval 2 Time elapsed : 299
    Interval 3 Time elapsed : 31
    Interval 4 Time elapsed : 5
    Interval 5 Time elapsed : 0
    Interval 6 Time elapsed : 0
    Interval 7 Time elapsed : 0
    Interval 8 Time elapsed : 0
    Interval 9 Time elapsed : 0
    Interval 10 Time elapsed : 0
    

    setInterval 不能很好地衡量下一次执行应该排队的时间。我认为这与睡眠()有关。如果一个进程正在占用 cpu 并挂起它(就像你做的那样),这是不可靠的。

    setInterval 只保证这一点:

    • 第 1 次执行不会在 250 毫秒内发生。
    • 第 2 次执行不会在 2*250 毫秒内发生。
    • 第 3 次执行不会在 3*250 毫秒内发生。
    • ...

    setInterval 不保证某些执行将在未来的确定时间之前发生。这取决于当前的 CPU 使用率。

    排队函数的排队和启动也取决于当前的 CPU 使用率。

    额外的常见建议 如果你需要定期执行一个函数,我推荐这种方法,保证执行之间的延迟总是大于 250 毫秒

    var t = false;
    var fun = function() {
        console.log("run");
        t = setTimeout(fun, 250);
    }
    fun();
    

    【讨论】:

    • 我对第二个代码很感兴趣......我看不出与第一个代码的区别,但输出明显不同
    • @GabrielMatusevich startTime = new Date(); 在函数顶部
    • 我在函数的第一行添加了startTime = new Date();,仅查看函数的经过时间,从 setinterval 测量时间没有副作用
    • 所以实际上函数正在排队......只是时间测量错误?
    • 谢谢你的解释,有类似的答案,但我认为这是第一个,更好的解释。
    【解决方案3】:

    好吧,我有几点要说,先看看结果。

    “新”代码:

    var limit        = 1000000000;
    var intervals    = 0;
    var totalTime    = new Date();
    
    top.uid = setInterval(
        function () {
            startTime    = new Date();
            // final lap?
            if (intervals == 9) clearInterval(top.uid);
    
            for (i = 0; i < limit; i += 1) {
                // just working the CPU
            }
            // reduce the iterations
            limit = limit / 10;
            intervals += 1;
    
            console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
            // reset the time
        }, 1);
    

    请注意,我从第 4 行中删除了变量 startTime 并将其传递到函数的开头,因此,计算的时间对于所有调用都是相同的。 我将 TIMER 表单从 250ms 更改为 1ms,因此您不必从每个结果中计算 -250ms。

    新的结果非常一致,表明现在计算所花费的时间非常可接受且连贯。

    希望我能帮您清理一下。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多