setTimeout / setInterval在背景标签中延迟1000毫秒(Chrome和Firefox)

时间:2013-10-20 09:34:40

标签: javascript google-chrome firefox settimeout setinterval

当事件使用setTimeout / setInterval排队,并且用户正在查看单独的标签时,Chrome和Firefox会在执行事件之前强制执行至少1000毫秒的延迟。 This article详细说明了行为。

之前已在StackOverflow上讨论过,但问题和答案仅适用于动画。显然,当用户重新进入选项卡时,可以强制动画更新到最新状态。

但该解决方案不适用于有序音频。我有Web Audio API按顺序播放多个音频文件,setTimeout用于倒计时播放下一个音频文件。如果你把标签放在后台,你会在每个模式之间产生令人讨厌的1秒间隙 - 这是专为高级音频设计的API中的一个极端缺陷。

您可以在各种HTML5序列发生器中见证此行为,例如:使用PatternSketch - 只需输入图案,播放并转到另一个标签页。

所以我需要一种解决方法:一种在没有1000ms钳位的情况下排队事件的方法。有谁知道一种方式?

  • 我能想到的唯一解决方案是让window.postMessage每隔一毫秒运行一次,并检查每次是否要执行该事件。这绝对不利于表现。这是唯一的选择吗?

  • 显然没有为Web Audio API规划事件系统,所以这是不可能的。

1 个答案:

答案 0 :(得分:2)

编辑:另一个答案是每个https://stackoverflow.com/a/12522580/1481489使用WebWorker - 这个答案有点具体,所以这里有更通用的内容:

interval.js

var intervalId = null;
onmessage = function(event) {
    if ( event.data.start ) {
        intervalId = setInterval(function(){
            postMessage('interval.start');
        },event.data.ms||0);
    }
    if ( event.data.stop && intervalId !== null ) {
        clearInterval(intervalId);
    }
};

和您的主程序:

var stuff = { // your custom class or object or whatever...
    first: Date.now(),
    last: Date.now(),
    callback: function callback() {
        var cur = Date.now();
        document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
        this.last = cur;
    }
};

var doWork = new Worker('interval.js');
doWork.onmessage = function(event) {
    if ( event.data === 'interval.start' ) {
        stuff.callback(); // queue your custom methods in here or whatever
    }
};
doWork.postMessage({start:true,ms:250}); // tell the worker to start up with 250ms intervals
// doWork.postMessage({stop:true}); // or tell it just to stop.

完全丑陋,但你可以打开一个子弹出窗口。但是,所有这一切都是将一些警告转移到子窗口,即如果子窗口最小化,则会出现1000ms问题,但如果它只是失焦,则没有问题。然后,如果它关闭,则它会停止,但用户只需再次单击开始按钮。

所以,我认为这并没有真正解决你的问题...但这是一个草稿:

var mainIntervalMs = 250;

var stuff = { // your custom class or object or whatever...
    first: Date.now(),
    last: Date.now(),
    callback: function callback(){
        var cur = Date.now();
        document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
        this.last = cur;
    }
};

function openerCallbackHandler() {
    stuff.callback(); // queue your custom methods in here or whatever
}

function openerTick(childIntervalMs) { // this isn't actually used in this window, but makes it easier to embed the code in the child window 
    setInterval(function() {
        window.opener.openerCallbackHandler();
    },childIntervalMs);
}

// build the popup that will handle the interval
function buildIntervalWindow() {
    var controlWindow = window.open('about:blank','controlWindow','width=10,height=10');
    var script = controlWindow.document.createElement('script');
    script.type = 'text/javascript';
    script.textContent = '('+openerTick+')('+mainIntervalMs+');';
    controlWindow.document.body.appendChild(script);
}

// write the start button to circumvent popup blockers
document.write('<input type="button" onclick="buildIntervalWindow();return false;" value="Start" />');

我建议你找一个更好的组织,写作等方法,但至少应该指出你正确的方向。它也应该在很多差异浏览器中工作(理论上,只在chrome中测试)。我会把你留给其他人。

哦,如果父母掉线,不要忘记自动关闭子窗口。