有没有比setTimeout和requestAnimationFrame更快的东西?

15

(我需要一个在浏览器上的process.nextTick等效物。)

我试图尽可能发挥JavaScript性能,所以我制作了一个简单的计数器……每秒钟我连续调用一个将一个变量加一的函数。

代码:codepen.io/rafaelcastrocouto/pen/gDFxt

我在谷歌浏览器/win7中使用setTimeout得到了大约250个,而使用requestAnimationFrame得到了70个。我知道requestAnimationFrame与屏幕刷新率一起使用,那么我们如何使它更快?

附注:我知道asm.js。


我不确定这是否做了同样的事情,但是请查看使用postMessage的这个hack:http://codepen.io/anon/pen/wczFv - Dogbert
setTimeout在大多数浏览器中的最小时间间隔为4毫秒,这就解释了为什么你得到的值约为250。 - Qantas 94 Heavy
@Dogbert,我在这里进行了测试https://www.quora.com/Does-JavaScript-in-the-browser-have-the-equivalent-of-process-nextTick-or-setImmediate-in-node-js-or-do-we-just-have-setTimeout/comment/6233464,似乎Mutation Observer胜出Promise。 - Pacerier
1
我怀疑animation-frame和post-message能否胜任mutation observer,但尚未经过测试。 - Pacerier
3个回答

32

嗯,有 setImmediate() 可以立即运行代码,就像你期望使用 setTimeout(0) 时的效果。

区别在于,setTimeout(0) 实际上并不会立即运行;setTimeout 被“夹紧”到最小等待时间(4ms),这就是为什么你在测试程序中只得到了250个计数器的原因。 setImmediate() 真的会立即运行,因此使用它进行计数测试将获得数量级更高的结果。

但是,您可能需要检查浏览器是否支持 setImmediate -- 它还没有在所有浏览器中提供。 (当然,您可以使用 setTimeout(0) 作为后备,但这样就回到了它所施加的最小等待时间)。

postMessage() 也是一种选择,可以实现几乎相同的结果,尽管它是一个更复杂的API,因为它旨在做比简单的调用循环更多的事情。此外,在使用它时需要考虑其他因素(请参阅链接的MDN文章以获取更多信息)。

MDN网站还提到了用于 setImmediate 的polyfill库,它使用postMessage和其他技术将setImmediate添加到尚未支持它的浏览器中。

使用requestAnimationFrame(),您应该得到60个计数器的结果,因为这是每秒帧数的标准值。如果你得到的比这多,那么你的程序可能运行了超过一秒。

你永远无法使用它获得高计数测试数字,因为它只会每秒触发60次(如果硬件刷新帧率由于某种原因更低,则可能更少)。但是,如果你的任务涉及更新显示,则这就足够了,因此你可以使用requestAnimationFrame()来限制它被调用的次数,从而释放程序中其他任务所需的资源。
这就是为什么存在requestAnimationFrame()的原因。如果你只关心让你的代码尽可能经常运行,那么不要使用requestAnimationFrame();改用setTimeoutsetImmediate。但这并不一定是性能最好的选择,因为它会占用浏览器需要用于其他任务的处理器资源。
归根结底,性能不仅仅是让某些东西尽可能多地运行;它还是使用户体验尽可能流畅。而这通常意味着对你的调用循环施加限制。

@rafaelcastrocouto:没问题。很高兴能帮忙。(嗯,给我点赞?你可以这样做,而且你已经这样做了……你点赞并选择接受答案,除非你指的是其他什么?) - Spudley
你应该得到额外的声望!恭喜! - rafaelcastrocouto
我注意到在某些情况下,requestAnimationFrame 会在提到的 YuzuJS/setImmediate shim 之前触发。我认为这可能是因为浏览器正在进行计算而导致的 - 在这种情况下,它可能会优先考虑平滑动画而不是严格的 postMessage 规范合规性。但这只是一种假设。顺便说一句,这些结果来自于基于 Chromium 构建的 electron - tomekwi
2
此功能非标准化且不在标准轨道上。请勿在面向Web的生产站点上使用它:它不能为每个用户工作。实现之间也可能存在较大的不兼容性,并且行为将来可能会更改。 - Andrew

7
最短的异步延迟可以使用 MutationObserver,但是如果您一直调用它,界面将永远没有更新的机会。因此,技巧是使用 MutationObserver 来增加值,同时偶尔使用 requestAnimationFrame 来更新UI,但这是不允许的。请参见http://jsfiddle.net/6TZ9J/1/
var div = document.createElement("div");
var count = 0;
var cur = true;
var now = Date.now();
var observer = new MutationObserver(function () {
    count++;
    if (Date.now() - now > 1000) {
        document.getElementById("count").textContent = count;
    } else {
        change();
    }

});

observer.observe(div, {
    attributes: true,
    childList: true,
    characterData: true
});

function change() {
    cur = !cur;
    div.setAttribute("class", cur);
}
change();

很棒的解决方案...添加到笔中...真遗憾你不喜欢比赛。 - rafaelcastrocouto

2

按照此博客中的描述使用postMessage()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接