Chrome:后台选项卡中暂停的超时/间隔?

问题描述 投票:0回答:8

我正在使用

这个测试
来测试setTimeout的准确性。现在我注意到(正如预期的那样)
setTimeout
不是很准确,但对于大多数设备来说并不是非常不准确。现在,如果我在 Chrome 中运行测试并让它在后台选项卡中运行(因此,切换到另一个选项卡并在那里浏览),返回测试并检查结果(如果测试完成),它们会发生巨大的变化。看起来超时运行得慢了很多。在 FF4 或 IE9 中测试,没有发生这种情况。

所以看起来 Chrome 会暂停或至少减慢没有焦点的选项卡中的 JavaScript 执行速度。在互联网上找不到太多有关该主题的信息。这意味着我们无法运行后台任务,例如使用 XHR 调用和

setInterval
定期检查服务器(我怀疑
setInterval
会看到相同的行为,如果有时间的话,我会编写一个测试) .

有人遇到过这种情况吗?对于这种暂停/减速是否有解决方法?你会称其为错误吗?我应该将其归档吗?

javascript google-chrome tabs settimeout
8个回答
126
投票

我最近问过这个问题,这是设计行为。当选项卡处于非活动状态时,每秒最多只能调用该函数一次。这是代码更改

也许这会有所帮助: 当 Chrome 中的选项卡处于非活动状态时,如何使 setInterval 也能工作?

TL;博士:使用Web Workers


37
投票

有一个使用 Web Workers 的解决方案,因为它们在单独的进程中运行并且不会减慢

我编写了一个小脚本,无需更改代码即可使用 - 它只是覆盖函数 setTimeout、clearTimeout、setInterval、clearInterval

只需将其包含在所有代码之前

http://github.com/turuslan/HackTimer


21
投票

播放空声音会强制浏览器保留性能。我在阅读此评论后发现了它:How to make JavaScript run at normal speed in Chrome Even while tab is not active?

该评论的来源在这里

Chromium 内部人士还澄清,对于所有“播放音频”的后台选项卡以及任何“存在活动 Websocket 连接”的页面,将自动禁用积极的节流。

对于使用 WebSocket 的浏览器游戏,我需要无限的性能点播,所以我从经验中知道,使用 WebSocket 并不能确保无限的性能,但从测试来看,播放音频文件似乎可以确保它

这是我为此目的创建的两个空音频循环,您可以在商业上自由使用它们: http://adventure.land/sounds/loops/empty_loop_for_js_performance.ogg http://adventure.land/sounds/loops/empty_loop_for_js_performance.wav

(它们包括-58db噪音,-60db不起作用)

我根据用户需求使用 Howler.js 来玩它们:https://github.com/goldfire/howler.js

function performance_trick()
{
    if(sounds.empty) return sounds.empty.play();
    sounds.empty = new Howl({
        src: ['/sounds/loops/empty_loop_for_js_performance.ogg','/sounds/loops/empty_loop_for_js_performance.wav'],
        volume:0.5,
        autoplay: true, loop: true,
    });
}

令人遗憾的是,默认情况下没有内置方法可以打开/关闭完整的 JavaScript 性能,但是,加密矿工可以使用 Web Workers 劫持您的所有计算线程,而无需任何提示。


4
投票

对于单元测试,您可以使用参数运行 chrome/chromium:

--disable-background-timer-throttling


3
投票

我发布了 worker-interval npm 包,其中 setInterval 和clearInterval 实现使用 Web-Workers 在 Chrome、Firefox 和 IE 的非活动选项卡上保持和运行。

大多数现代浏览器(Chrome、Firefox 和 IE)的间隔(窗口计时器)被限制为在非活动选项卡中每秒触发一次。

您可以找到更多信息

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Timeouts_and_intervals


0
投票

我将 jQuery 核心更新到 1.9.1,它解决了非活动选项卡中的间隔差异。我会先尝试一下,然后再研究其他代码覆盖选项。


0
投票

浏览器旨在优化用户体验和电池寿命,它们限制后台选项卡的活动以节省 CPU 和电量。

躲避后台执行抑制的2种方法是 (ping/pong 消息示例)

第一 -

内联:

// Web Worker code defined as a string
const workerCode = `
// When the worker receives a message...
    onmessage = function(e) {
        // ...if that message is 'start'
        if (e.data === 'start') {
            // ...set an interval to send a heartbeat every minute
            setInterval(() => {
                // Fetch a heartbeat from the server
                fetch('http://127.0.0.1:9000/heartbeat')
                .then(response => {
                    // If the response isn't okay, throw an error
                    if (!response.ok) {
                        throw new Error('Network response was not ok');
                    }
                    return response.json(); 
                })
                .then(data => {
                    // Log the received heartbeat
                    console.log('Heartbeat received:', data);
                })
                .catch(error => {
                    // If there's an error, log it
                    console.error('Fetch error:', error.message);
                });
            }, 60000);
        }
    };
`;

// Create a new Blob object from the worker code
const blob = new Blob([workerCode], { type: 'application/javascript' });

// Create a new Web Worker from the blob URL
const worker = new Worker(URL.createObjectURL(blob));

// Post a 'start' message to the worker to begin the heartbeat
worker.postMessage('start');

第二-

worker.js 文件:

// When the worker receives a message...
onmessage = function(e) {
    // ...if that message is 'start'
    if (e.data === 'start') {
        // ...set an interval to send a heartbeat every minute
        setInterval(() => {
            // Fetch a heartbeat from the server
            fetch('http://127.0.0.1:9000/heartbeat')
            .then(response => {
                // If the response isn't okay, throw an error
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                return response.json(); 
            })
            .then(data => {
                // Log the received heartbeat
                console.log('Heartbeat received:', data);
            })
            .catch(error => {
                // If there's an error, log it
                console.error('Fetch error:', error.message);
            });
        }, 60000);
    }
};

主要部分:

// Create a new Web Worker from the external worker file
const worker = new Worker('worker.js');

// Post a 'start' message to the worker to begin the heartbeat
worker.postMessage('start');

-1
投票

这是我的解决方案,它获取当前毫秒,并将其与创建函数的毫秒进行比较。对于间隔,它将在运行该函数时更新毫秒。您还可以通过 id 获取间隔/超时。

<script>

var nowMillisTimeout = [];
var timeout = [];
var nowMillisInterval = [];
var interval = [];

function getCurrentMillis(){
    var d = new Date();
    var now = d.getHours()+""+d.getMinutes()+""+d.getSeconds()+""+d.getMilliseconds();
    return now;
}

function setAccurateTimeout(callbackfunction, millis, id=0){
    nowMillisTimeout[id] = getCurrentMillis();
    timeout[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisTimeout[id] + +millis)){callbackfunction.call(); clearInterval(timeout[id]);} }, 10);
}

function setAccurateInterval(callbackfunction, millis, id=0){
    nowMillisInterval[id] = getCurrentMillis();
    interval[id] = setInterval(function(){ var now = getCurrentMillis(); if(now >= (+nowMillisInterval[id] + +millis)){callbackfunction.call(); nowMillisInterval[id] = getCurrentMillis();} }, 10);
}

//usage
setAccurateTimeout(function(){ console.log('test timeout'); }, 1000, 1);

setAccurateInterval(function(){ console.log('test interval'); }, 1000, 1);

</script>
© www.soinside.com 2019 - 2024. All rights reserved.