我正在使用MIDI.js与几种乐器一起播放MIDI文件。
以下内容执行太晚我怎样才能解决这个问题呢?
- 歌曲的第一个音符。像所有的笔记一样,它们是通过
AudioBufferSourceNode
的start()
来安排的。 - MIDI程序变更事件。他们是通过这里的
setTimeout
安排的。他们的"lateness">
当我停止歌曲并重新开始时,不再有问题,但delay
值非常相似。因此,delay
值可能不是问题的原因。
(我使用最新的官方分支(命名为" abcj"),因为"大师"分支较旧,并且在此类MIDI文件中存在更多问题。)
JavaScript事件循环就是这样工作的。
呼叫
setTimeout
…在给定的时间间隔后不执行回调函数。执行取决于队列中等待任务的数量。
…延迟是运行时处理请求所需的最小时间(不是保证时间)。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop zero_delays
您可以使用window.requestAnimationFrame()
来代替setTimeout()
,并自行计算延迟的经过时间。
windows . requestanimationframe () - Web api | MDN
window.requestAnimationFrame()
方法告诉浏览器您希望执行动画,并请求浏览器在下次重绘之前调用指定的函数来更新动画。该方法接受一个回调函数作为参数,在重新绘制之前调用。…将请求在浏览器执行下一次重绘之前调用动画函数。回调的次数通常为每秒60次,但通常会匹配大多数web浏览器中的显示刷新率
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
performance.now() - Web api | MDNhttps://developer.mozilla.org/en-US/docs/Web/API/Performance/now
在我们的情况下,我们不想做任何动画,而只想使用它来获得更高精度的超时。
const delayMs = 1000;
const startTime = performance.now();
function delay(func) {
const delayStartTime = performance.now();
function delayStep() {
// Run again if still elapsed time is less than a delay
if (performance.now() - delayStartTime <= delayMs) {
window.requestAnimationFrame(delayStep);
}
else
{
// Run the delayed function
func();
}
}
// Run first time
window.requestAnimationFrame(delayStep);
}
// Trying `setTimeout()`
setTimeout(() => doSomeJob('setTimeout()'), delayMs);
// Trying `delay()`
delay(() => doSomeJob('delay()'));
// Function that we'd like to run with a delay
function doSomeJob(marker)
{
const elapsedTime = performance.now() - startTime;
console.log(`${marker}: Ran after ${elapsedTime / 1000} seconds`);
}
如果你多次运行它,你会发现delay()
几乎总是比setTimeout()
好。差异非常小,因为页面上没有发生任何其他事情。如果有一些密集的运行,setTimeout()
应该表现出更差的"精度"。