7

当事件与 setTimeout/setInterval 一起排队,并且用户正在查看单独的选项卡时,Chrome 和 Firefox 在事件执行前强制执行至少 1000 毫秒的延迟。本文详细介绍了这种行为。

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

但该解决方案不适用于序列化的音频。我有 Web Audio API 按顺序播放几个音频文件,并且 setTimeout 用于倒计时到下一个音频文件播放的时间。如果将选项卡放在后台,每个模式之间会出现令人讨厌的 1 秒间隔——这是为高级音频设计的 API 中的一个极端缺陷。

您可以在各种 HTML5 序列器中看到这种行为,例如使用PatternSketch —— 只需输入一个模式、播放并转到另一个选项卡。

所以我需要一种解决方法:一种没有 1000 毫秒限制的事件排队方法。有谁知道一种方法?

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

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

4

1 回答 1

3

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

间隔.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.

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

所以,我想这并不能真正解决你的问题......但这是一个粗略的草案:

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 中测试过)。剩下的就交给你了。

哦,别忘了在父窗口掉线时自动关闭子窗口。

于 2013-10-22T03:55:50.910 回答