还有比setTimeout和requestAnimationFrame更快的吗?

问题描述 投票:0回答:3

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

我试图充分利用 javascript 性能,所以我做了一个简单的计数器...... 一秒钟后,我连续调用一个函数,该函数仅向变量加一。

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

我在 google chrome / win7 中使用 setTimeout 得到了大约 250 个,使用 requestAnimationFrame 得到了 70 个。 我知道 requestAnimationFrame 与屏幕刷新率相关,那么我们如何才能使其更快?

PS:我知道asm.js

javascript performance settimeout requestanimationframe
3个回答
32
投票

嗯,有

setImmediate()
可以立即运行代码,即正如您所期望的
setTimeout(0)
那样。

不同之处在于

setTimeout(0)
实际上并不立即运行;
setTimeout
被“限制”为最短等待时间(4 毫秒),这就是为什么您在测试程序中只能得到 250 的计数。
setImmediate()
确实会立即运行,因此使用它您的计数器测试将会提高几个数量级。

但是,您可能想检查浏览器对

setImmediate
的支持——它尚未在所有浏览器中可用。 (当然,您可以使用
setTimeout(0)
作为后备,但随后您又回到了它所规定的最短等待时间)。

postMessage()
也是一个选项,并且可以实现大致相同的结果,尽管它是一个更复杂的 API,因为它的目的不仅仅是简单的调用循环。另外,使用它时还需要考虑其他注意事项(有关更多信息,请参阅链接的 MDN 文章)。

MDN 网站还提到

setImmediate
的 polyfill 库,它使用
postMessage
和其他技术将
setImmediate
添加到尚不支持它的浏览器中。

使用

requestAnimationFrame()
,您的测试程序应该得到 60,因为这是每秒的标准帧数。如果您获得的时间超过这个值,那么您的程序运行的时间可能超过一秒。

使用它在计数测试中永远不会得到很高的数字,因为它每秒仅触发 60 次(如果硬件刷新帧速率由于某种原因较低,则触发次数更少),但如果您的任务涉及更新display 那么这就是您所需要的,因此您可以使用

requestAnimationFrame()
来限制调用它的次数,从而为程序中的其他任务释放资源。

这就是

requestAnimationFrame()
存在的原因。如果您关心的只是让代码尽可能频繁地运行,那么就不要使用
requestAnimationFrame()
;请使用
setTimeout
setImmediate
代替。但这不一定是性能最好的事情,因为它会消耗浏览器执行其他任务所需的处理器能力。

最终,性能不仅仅是让某个东西运行最多次数;而是让某个东西运行最多次数。这是为了让用户体验尽可能流畅。这通常意味着对您的调用循环施加限制。


7
投票

异步时最短的延迟来自

MutationObserver
,但它太短了,如果你继续调用它,UI 将永远没有机会更新。

因此,技巧是使用

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();

2
投票

按照

此博客
中所述使用postMessage()

var timeouts = [];
var messageName = "zero-timeout-message";

// Like setTimeout, but only takes a function argument.  There's
// no time argument (always zero) and no arguments (you have to
// use a closure).
function setZeroTimeout(fn) {
    timeouts.push(fn);
    window.postMessage(messageName, "*");
}

function handleMessage(event) {
    if (event.source == window && event.data == messageName) {
        event.stopPropagation();
        if (timeouts.length > 0) {
            var fn = timeouts.shift();
            fn();
        }
    }
}

window.addEventListener("message", handleMessage, true);

postMessage
现在(2023 年)拥有出色的浏览器支持。

© www.soinside.com 2019 - 2024. All rights reserved.