setTimeout花费的时间比应该的长

Rad*_*uch 2 javascript firefox greasemonkey settimeout setinterval

有人可以解释一下为什么脚本(下面带有setTimeout命令)在Greasemonkey中执行所需的时间(400-500毫秒)比在Firefox控制台中执行的时间长100毫秒(400-500毫秒)吗?

var start = new Date ().getTime ();
console.log (
    new Date ().getHours ()+" : " + new Date ().getMinutes () 
    + " : " + new Date ().getSeconds () + " : " 
    + new Date ().getMilliseconds () 
); 

setTimeout (foo,100);
//foo ();

function foo () {
    var time = new Date () - start;
    console.log ("Execution time: " + time + "ms");
}
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为如果我切换setTimeout(foo,100)为pure foo(),那么Greasemonkey和Firefox控制台都将以闪电般的速度(〜10毫秒)执行它。

Bro*_*ams 5

实际上,在我的便笺系统(Win XP,FF 28.0,GM 1.15)上,这与Greasemonkey无关,而与(A)控制台以及(B)Firefox和/或您的计算机在做什么一样。

JavaScript计时器非常糟糕。(另请参见John Resig的“ JavaScript时间的准确性”。)

您的结果基于很小的样本量,并且没有足够的数据来开始看到准确的图片。浏览器和Greasemonkey在此问题方面也一直在变化,因此您的版本可能有所不同。

如果使用间隔,例如:

setInterval (foo, 100);

function foo () {
    var time = new Date () - start;
    console.log ("Execution time: " + time + "ms");
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以开始收集一些统计数据,看看可能会发生什么。

完善该代码并添加统计信息将得到如下用户脚本:

// ==UserScript==
// @name        _Greasemonkey timing test
// @include     /sf/ask/1591694541/*
// @grant       none
// ==/UserScript==
// @grant       GM_addStyle

/*--- Test:
        Both grant modes
        FF console
        Firebug console
        Embedded in page.
*/
var numMeasurements = 100;
var measurementList = [];
var startDate       = new Date ();
var startTime       = startDate.getTime ();

console.log (
    startDate.getHours ()+ " : " + startDate.getMinutes () + " : "
    + startDate.getSeconds () + " : " + startDate.getMilliseconds ()
);

var startDate   = new Date ();  //-- Record time just before interval start.
//setTimeout (timelog, 100);
/*--- WARNING: for delays less than about 50, system "granularity" and
    overhead effects will distort the results even more.
*/
var logTimer    = setInterval (timelog, 100);

function timelog () {
    timelog.numloops    = timelog.numloops  ||  0;

    if (timelog.numloops >= numMeasurements) {
        console.log ('===> Reached ' + timelog.numloops + ' loops.');
        clearInterval (logTimer);

        //--- Calculate stats:
        var stats           = {};
        stats.min           = Number.MAX_VALUE; //-- Always start at opposite
        stats.max           = Number.MIN_VALUE;
        stats.sum           = 0;
        stats.mean          = 0;
        stats.sumSqrs       = 0;
        stats.stdDev        = 0;
        stats.N             = measurementList.length;

        for (var J = 0;  J < stats.N;  ++J) {
            var measVal     = measurementList[J];
            stats.sum      += measVal;
            stats.sumSqrs  += measVal * measVal;

            if (measVal > stats.max)    stats.max = measVal;
            if (measVal < stats.min)    stats.min = measVal;
        }

        stats.mean          = stats.sum / stats.N;
        stats.stdDev        = Math.sqrt (
            (stats.sumSqrs / stats.N) - (stats.mean * stats.mean)
        );


        //--- Display stats:
        var decsToDisplay   = 1;
        console.log (' Measurements: ' + stats.N);
        console.log ('      Average: ' + stats.mean.toFixed (decsToDisplay) );
        console.log ('   Min to Max: ' + stats.min + ' to ' + stats.max);
        console.log ('Std Deviation: ' + stats.stdDev.toFixed (decsToDisplay) );
    }
    else {
        timelog.numloops++;
        var timeNow = new Date ();
        var timeDif = timeNow - startDate;
        measurementList.push (timeDif);
        console.log (
            '==> Execution time ('
              //-- Left-pad value for more legible column, 3 chars wide.
            + ("  " + timelog.numloops).slice (-3) + '): '
              //-- Left-pad value for more legible column, 4 chars wide.
            + ("   " + timeDif).slice (-4) + ' ms   '
            , timeNow.getTime ()
        );
        startDate   = timeNow;
    }
}
Run Code Online (Sandbox Code Playgroud)


安装脚本和/或您可以在jsFiddle上看到运行中的代码

要查看Greasemonkey是否是一个因素,我们至少应该测试以下情况:

  1. Firefox控制台中的代码。
  2. 将网页中的代码同时发送到控制台和关闭的控制台。
  3. 在Greasemonkey脚本中的代码中,沙箱处于活动状态(已@grant GM_addStyle设置)。
  4. 使用Greasemonkey脚本@grant none激活的代码。
  5. Firebug控制台中的代码。

理想情况下,网页和系统环境应保持尽可能恒定。

经过100毫秒的延迟和100个样本的测试(可能是有意义数据的最小值),我得到了(所有值以毫秒为单位):

//-这些首先针对stackoverflow.com/q/22738493

                                                  标准
条件最小最大平均偏差
-------------------------- --- --- ----- ---------
Firefox控制台,运行1:0 518 138.9 133.2
Firefox控制台,运行2:1 466 215.4 209.6

Firebug控制台,运行1:1 144 100.5 21.8
Firebug控制台,运行2:3 209 100.9 25.2

GM至FF利弊,在沙盒中:0 398 135.4 112.9
GM到FF缺点,@ grant无1:0 387 125.3 97.4
GM到FF缺点,@ grant无2:0 563 145.2 122.0
GM至Firebug控制台:38 401 109.4 49.1

//-这些针对jsfiddle.net/caL94/2运行

jsFiddle至FF控制台1:2 375 113.3 82.5
jsFiddle至FF控制台2:1 575 169.7 171.1
jsFiddle到Firebug控制台:27219 103.5 24.9

jsFiddle,控制台已关闭1:0 530 105.3 57.2
jsFiddle,控制台关闭了2:5 195 100.0 21.9


从这些数字中,应该清楚:

  1. JavaScript计时器不利于精确计时。 (但它们可能足以满足大多数实际的网页使用。)
  2. Greasemonkey中的计时代码与在Firebug控制台中运行或未登录到任何控制台的代码一样好。
  3. 真正的大热门是登录Firefox的控制台(CtrlShiftK),这从根本上降低了计时器的准确性。
  4. 计算机和浏览器(甚至是网页)在很大程度上都影响着准确性和可重复性。
  5. 另外,请记住,如果您离开标签页(失去焦点),浏览器就会限制javascript计时器