当前位置: 首页 > 知识库问答 >
问题:

setTimeout需要比它应该的时间更长

韶云瀚
2023-03-14

有人能解释一下为什么下面带有setTimeout命令的脚本在Gresemonkey中的执行时间(400-500毫秒)比在火狐控制台(正好是100毫秒)长得多吗?

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");
}

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

共有1个答案

潘智刚
2023-03-14

事实上,在我的抓取系统(Win XP,FF 28.0,GM 1.15)上,这与Gresemonkey几乎没有关系,一切都与(A)控制台和(B)火狐和/或你的机器还在做什么有关。

JavaScript计时器是出了名的糟糕。(另请参见John Resig的“JavaScript时间的准确性”)

你的结果是基于一个非常小的样本量,没有足够的数据开始看到一个准确的图片。此外,浏览器和Greasemonkey在这个问题上一直在变化,所以您的版本可能会有所不同。

如果我们使用间隔,例如:

setInterval (foo, 100);

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

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

细化代码并添加统计信息会生成如下用户脚本:

// ==UserScript==
// @name        _Greasemonkey timing test
// @include     https://stackoverflow.com/questions/22738493/*
// @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;
    }
}


安装脚本和/或您可以在jsFiddle上看到这段代码。

为了确定Greasemonkey是否是一个因素,我们至少应该测试以下场景:

  1. Firefox控制台中的代码。
  2. 在网页中编码到控制台和关闭控制台。
  3. 格雷斯蒙克脚本中的代码与沙盒活动(@GrantGM_addStyleset)。
  4. 代码在一个格雷斯蒙克脚本与@授予无活动。
  5. Firebug控制台中的代码。

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

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

//-- These first were run against stackoverflow.com/q/22738493

                                                  Std
Condition                      Min  Max   Avg   Deviation
--------------------------     ---  ---  -----  ---------
Firefox console, run 1:          0  518  138.9    133.2
Firefox console, run 2:          1  466  215.4    209.6

Firebug console, run 1:          1  144  100.5     21.8
Firebug console, run 2:          3  209  100.9     25.2

GM to FF cons, in sandbox:       0  398  135.4    112.9
GM to FF cons, @grant none 1:    0  387  125.3     97.4
GM to FF cons, @grant none 2:    0  563  145.2    122.0
GM to Firebug console:          38  401  109.4     49.1

//-- These were run against jsfiddle.net/caL94/2

jsFiddle to FF console 1:        2  375  113.3     82.5
jsFiddle to FF console 2:        1  575  169.7    171.1
jsFiddle to Firebug console:    27  219  103.5     24.9

jsFiddle, consoles closed 1:     0  530  105.3     57.2
jsFiddle, consoles closed 2:     5  195  100.0     21.9


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

  1. JavaScript定时器不适合精确计时。(但他们可能是足够的最实际的网页使用。)
  2. 在格雷斯蒙基中的定时代码的性能与在Firebug控制台中运行或不登录到任何控制台的代码一样好。
  3. 最大的打击是登录火狐的控制台(CtrlShiftK)-这从根本上降低了计时器的准确性。
  4. 计算机和浏览器(甚至网页)在很大程度上影响了所有的准确性和可重复性。
  5. 此外,请记住,如果您从标签页切换(失去焦点),浏览器会限制javascript定时器。
 类似资料:
  • 问题内容: 我正在尝试加速我的代码,这部分给我带来了问题, 我尝试使用Cython,然后按照此处给出的建议进行操作,但是我的纯python函数的性能优于cython和cython_optimized函数 cython代码如下: 然后,我运行以下命令: 结果如下: 对于纯python: 对于非优化的cython: 对于优化的: 我究竟做错了什么 ? 谢谢你的帮助, 问题答案: 使用Numba的解决方

  • 我在我的第一个java程序中扫描用户输入时遇到了一些麻烦。当我编译并运行它时,会立即提示输入(即命令行停止并闪烁)。当我输入任何东西时,第一行被打印出来,要求我输入一个整数。然后打印第二行,并提示我输入另一个值。 这个程序的输出是我输入的前两个值。这很难解释,但它基本上要求3个输入值,只使用两个。

  • 我有一个字体大小的问题,它比它应该的要小得多,即使增加大小参数也没有什么区别。 首选的大小是为了显示它不是限制它的大小,而文本是随机的和长的,以显示文本有多小。它是所有字体,不仅没有衬线,但如果我使用系统默认字体,这是正常的。我还重新启动了电脑,以确保它没有错误地将字体加载到缓存中。 Panel只是一个具有正常流程布局的JPanel。 以下是该文本的图像: 文本大约5像素高。谢谢

  • 好的,我有一个带屏幕的JFrame。我把尺寸设定为800乘800。但是,创建的窗口比该窗口小。任务栏没有问题,因为它不是全尺寸的。 在screen类中,paint方法在边框应位于的位置周围绘制一个框: 当我运行它时,窗口比盒子小,底部和右侧被切断。 请注意,我手动调整了第二张图片的大小,以显示边框的差异。 我意识到我已经把盒子的每一面都画小了1个像素,但差别远不止2个像素。

  • 我在看一个关于基本Java游戏编程的视频,看到了这段代码 在我看来,通过在render方法之外声明引用“bs”,代码运行得更快,这样就不会在每一帧都重新创建它,但我自己实际测试了它,发现情况恰恰相反。 运行了几次之后,就可以明显看出loop1平均花费的时间要长一个数量级。仅仅访问一个实例字段比创建一个新变量需要更长的时间,这似乎有悖于直觉。是我错过了什么还是就这么简单?如何才能了解哪些等效操作需要

  • 问题内容: 在工作中,我有一张大桌子(大约300万行,例如40-50列)。有时我需要清空一些列,并用新数据填充它们。我没想到的是 与用例如从同一表的其他列在sql查询中生成的数据或从子查询中的其他表查询生成的数据填充该列相比,花费的时间要多得多。不管我一次遍历所有表行(如上面的更新查询中),还是我使用光标逐行遍历表(使用pk)都没有关系。无论是在工作中使用大型表还是创建小型测试表并将其填充成千上万