如何在javascript中使用setTimeout考虑滞后

时间:2014-07-22 15:34:12

标签: javascript jquery settimeout blocking lag

setTimeout(func,delay)似乎在指定的时间非常精确地触发,只要该页面在尝试触发该函数时没有运行某些脚本。但有没有办法把滞后考虑在内?

例如,如果我设置3秒超时并且javascript运行一些繁重的代码,这会使页面长时间显示。只要处理“重码”在3秒内完成,它将在~3秒后超时。

有没有办法将“重代码”处理时间考虑在内并在3秒+页面被阻止之后超时?

这是一个jsfiddle:http://jsfiddle.net/me2loveit2/mCj2J/

var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100

function test() {
    var timeAfter100MS = new Date().getTime();
    $('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}

function block() {
    for (var i = 0; i < 100000000; i++) {};
}
block();
block();
block();

var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');

5 个答案:

答案 0 :(得分:2)

正如@adeneo指出的那样,没有这种可能性。您根本无法知道处理器在另一端运行代码的效率如何,或者它当前正在进行的任务可能会进一步减慢它的速度。每个案例都不同。 setTimeout尝试匹配指定的时间,但很多时候,它只是不准确。

我认为解决方案只是改变你的心态。 尽量避免长时间阻止同步操作,例如for (var i = 0; i < 10000000; i++) {};当您删除或修改这些操作时,您可以获得更准确的setTimeout触发。原因是,事件队列中会有较小的可执行块。

一般来说,有不同的方法来处理阻塞事件。例如,您可以查看Web worker或生成setTimeout调用。 (参见本文末尾的链接)。

因此,我不知道您的具体情况,但如果您尝试进行许多setTimeout调用,就像在游戏编程(循环)中那样解决方案是尝试更改将来的setTimeout调用以包含更小的值所以完整循环将尝试赶上模拟以匹配特定的帧速率。

这通常与requestAnimationFrame的组合完成。

尝试在浏览器中运行30 fps的循环的简短示例:

您还可以在 js fiddle

中查看
/**
 * This is example to run loop with 30fps in the browser
 *
 */

var gl = {
    now: new Date().getTime(),
    dt: 0.0,
    last: new Date().getTime(),
    // physics with 0.033333 steps
    step: 1 / 30
},
    frames = 0,
    started = new Date().getTime();

/**
  * Game loop
  *
  */
var gameLoop = function () {

    gl.now = new Date().getTime();
    gl.dt = gl.dt + Math.min(1, (gl.now - gl.last) / 1000);

    while (gl.dt > gl.step) {
        gl.dt = gl.dt - gl.step;

        // Increase frames
        frames++;

        if(frames === 30) {

            // How long it took to execute 30 frames in 1000 ms ?
            document.body.innerHTML = "We executed 30 frames in " + (new Date().getTime() - started) + " ms.";
            started = new Date().getTime();
            frames = 0;

        }  

    }

    // last
    gl.last = gl.now;

    // next
    requestAnimationFrame(gameLoop);

};

// Start the game loop
gameLoop();

希望这给了你一些想法。因此,不要忘记使用css过渡和类似的应用。

如需进一步阅读,我建议:

干杯。

答案 1 :(得分:0)

我不确定我是否100%理解这个问题,但是如果你做了这样的事情,你就可以看到在超时运行时是否还没有完成其他事情(重处理)。这应该需要大约5秒,如果你将2000000000切换到20000(减少处理),它应该在3秒后回来。

var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100
var currentTime = new Date().getTime();

function test() {
    if (currentTime - timeStart < 3000){
    var timeAfter100MS = new Date().getTime();
        $('body').append('Took less than 3 seconds - ' + (timeAfter100MS - timeStart)+"ms");
    }else{
            $('body').append('Took more than 3 seconds');
    }
}

function block() {

    for (var i = 0; i < 10000000; i++) {};
      currentTime = new Date().getTime();

}
block();
block();
block();


var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');

答案 2 :(得分:0)

如果&#39;重要&#39;代码导致非常明显的延迟,精确的时序很重要,您可以通过使用两次超时来保持精度。第一个超时测量滞后并相应地设置第二个超时。

以下为例,以您的代码为基础:

var timeStart = new Date().getTime();
var msDelay = 3000;
setTimeout(testLag, msDelay - 500);

function testLag() {
    var timeTestLag = new Date().getTime();
    $('body').append('testLag() fired at: ' + (timeTestLag - timeStart) + 'ms<br/>');
    setTimeout(test, timeStart + msDelay - timeTestLag);
}

function test() {
    var timeAfter100MS = new Date().getTime();
    $('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}

function block() {
    for (var i = 0; i < 1000000000; i++) {};
}
block();
block();
block();
block();
block();

var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');

请注意,块比你的块强得多 - 我为你的100000000添加了一个零,并添加了几个额外的block()调用。您可能需要调整数字以获得适合您自己机器的阻止级别。

答案 3 :(得分:0)

根据Mauno的答案,我想出了一个解决方案,暂时“跟踪延迟”使用间隔。我设置一个间隔很短的间隔来捕获延迟并在必要时设置另一个超时。以下是工作示例:http://jsfiddle.net/me2loveit2/mCj2J/14/

它是近似的,但总是在目标100毫米内的墙壁对我来说足够好。如果我提高间隔率可能会更准确,但我得到的对我来说已经足够了。

我知道使用超时&amp;间隔不是最好的,但有时是唯一的方法。我只是在页面加载上使用它们几秒钟就是这样。

以下是代码:

var timeStart = new Date().getTime();
var aditionalTimeout = 0;
var myTimeout;
setTimer(3000);
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');

function setTimer(milliseconds) {
    //allow additional time to account for the huge lag the page has on load
    recoverLagTime(milliseconds);
    myTimeout = setTimeout(function () {
        if (!aditionalTimeout) {
            test();
        } else {
            if (aditionalTimeout >= milliseconds) {
                test();
                return;
            }
            setTimer(aditionalTimeout);
        }
    }, milliseconds);
}

function recoverLagTime(timeoutTime) {
    aditionalTimeout = 0;
    var interval = 50;
    var counter = Math.ceil(timeoutTime / interval);
    var startTime = new Date().getTime();
    var intervalTime;
    var lagInterval = setInterval(adjustTimer, interval);

    function adjustTimer() {
        if (counter <= 0 || aditionalTimeout < 0) {
            clearInterval(lagInterval);
            return;
        }
        counter--;
        intervalTime = new Date().getTime();
        var diff = (intervalTime - startTime);
        if (diff > (interval + 5)) {
            aditionalTimeout += (diff - interval);
        }
        startTime = new Date().getTime();
    }
}

function test() {
    aditionalTimeout = -100;//stop the check function
    var timeAfter100MS = new Date().getTime();
    $('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000 + ~amount blocked)');
}

function block() {
    for (var i = 0; i < 100000000; i++) {};
}

答案 4 :(得分:0)

我正是为了这些目的写了一个小的(2个文件)库:每当CPU有空闲时间(使用requestAnimationFrame)时通过将代码拆分成较小的迭代来运行繁重的代码,因此它不会阻止整个应用程序分配特定百分比的CPU时间来执行代码,并使用余下的时间来执行其他脚本/更新UI。

它的功能与其他答案类似,但可能对您很方便,因为您可以轻松计算执行之间的经过时间,如果您需要知道这些数字(或使用它来利用您的应用程序中的操作,那就像那样)是为什么写的)

https://github.com/igorski/zThreader