在不挂起浏览器的情况下执行“实时”javascript

时间:2010-10-27 13:24:05

标签: javascript real-time

我正在尝试用Javascript编写一个简单的音乐序列器。

声音将由SoundManager2

播放

我很快意识到setTimeout和setInterval对于这种时序没用。它们的准确性根本不够好。

所以我正在尝试的是:

创建一个声音事件队列(一个二维数组[音符时间])

在while循环中处理队列

在伪代码中,它可能如下所示:

// The queue of time/note values (millisecs)
var q = [[0, C], [250, D], [500, E]]

var begin = (new Date).getTime()

while(q.length > 0 ){
    var now = (new Date).getTime()
    var eventTime = q[0][0] + begin

    if( now >= eventTime){

        playNote(q[0][1]) // Play the note  
        q.shift()       // Now that the note has been played, discard it.
    }
}

通过调试我发现这个方法看起来足够精确(playNote调用是在它应该的确切时间进行的。)

然而,当'序列'正在播放时,所有其他Javascript(包括实际发出声音的位)被暂停,这几乎不会让人感到惊讶。

这意味着我已经沉默了很长时间来运行序列,然后执行所有对playNote的调用。

我已经尝试在它自己的函数中隔离while循环,然后通过setTimeout调用它,希望这会创建一个后台线程来执行它自己的事情(即播放序列)而不会停止所有其他JS执行。这不起作用

我也尝试使用setTimeout调用playNote函数,希望尽管UI确实已经冻结,但至少序列正在正常播放,但这也不起作用。

我也试过一个组合,但你可能已经猜到了,这也不起作用。

所以我的问题是:

如何处理具有精确计时的事件队列,而不是在运行期间关闭所有其他代码执行

2 个答案:

答案 0 :(得分:1)

我不知道是否有旧浏览器的解决方案,但web workers是用于在JavaScript中进行并行执行。

最新版本的Chrome和Firefox支持它们,我不了解其他浏览器。

答案 1 :(得分:0)

如果用递归函数替换while循环(它不必自行执行),浏览器就不会冻结。

(function foo (q) {
    var now = (new Date).getTime(),
        eventTime = q[0][0] + begin;
    if (q.length > 0) {
        if (now >= eventTime) {
            playNote(q[0][1]); // Play the note
            q.shift(); // Discard played note
        }
        return foo(q); // Pass q into the next round
    } else {
        return; // End the "loop"
    }
}(q));

修改:递归调用+定时器准确性问题应由setInterval处理:

var timer = setInterval(function foo () {
    var now = (new Date).getTime(),
        eventTime = q[0][0] + begin;
    if (q.length > 0) {
        if (now >= eventTime) {
            playNote(q[0][1]); // Play the note
            q.shift(); // Discard played note
        }
    } else {
        clearInterval(timer); // End the "loop"
    }
}, 10); // This is in ms (10 is just a random number)