requestAnimFrame与普通的settimeout JavaScript相比有何不同?

3

在编写脚本时,我发现我的ajax调用次数比预期多了10倍,于是我添加了一个“fps计数器”,发现Chrome每秒运行约130次,而IE/FF/Opera每秒只运行约35帧。使用以下设置:

function load(){ 
//other code that does not effect this
    requestAnimFrame(function(){
        load();
    });
    debug();//function i was using to catch fps
}

window.requestAnimFrame = (function(callback){
    return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback){
        window.setTimeout(callback, 100);
    };
})();

现在的Google Chrome似乎不关心我放入window.setTimeout(callback, 100);中的值,可以是1,也可以是10000000,它都以约130帧运行,其余部分似乎足够接近(始终约为预期的3倍)。 我已经确保我没有多次调用load()。
另一方面,我曾经使用setTimeout(load,100); 当我改用这种方式时,所有浏览器开始以约30fps运行。
function load(){ 
    //other code that does not effect this

        debug();//function i was using to catch fps
        setTimeout(load, 100);
    }

这样做是不好的惯例还是过时了吗?问题是我并不确定为什么要使用requestAnimFrame,除了所有我在网上找到的示例都使用它,并且似乎可以实现给定的目标。我一遍又一遍地触发load事件只是为了检查是否有新信息要传输。
编辑:对于未来阅读此内容的人,请注意。在逐行运行代码时,我发现了一个逻辑错误,这解释了我的三倍时间。我确保没有多次调用load()函数。
function connected(result){
    if (xmlhttp.readyState==4 && xmlhttp.status==200){
        //code that does not matter for this
    }else if (xmlhttp.status==404){
        alert("epic failure i lost the page");
    }
    d_time = new Date().getTime();
    load();
}

Connect被xmlhttp.onreadystatechange=connected调用,因此它可以多次触发运行3组load(); 我需要做的是:

function connected(result){
    if (xmlhttp.readyState==4 && xmlhttp.status==200){
        d_time = new Date().getTime();
        load();
    }else if (xmlhttp.status==404){
        alert("epic failure i lost the page");
    }

}

所以Schmit和Patrick都在这里提供了帮助。Schmit让我知道我是个新手并且做错了。Patrick帮我意识到了那个函数发生了什么。


2
100毫秒将是每秒10帧...我猜测您的“fps计数器”的代码不正确。 - Shmiddty
不是d_count/((new Date().getTime() - d_time)/1000)。d_time在脚本第一次运行时设置,每次运行load()时d_count都会自增,这是我检查的第一件事。此外,相同的代码在所有浏览器上运行,所以Chrome仍然与其他浏览器不同。 - Noname Provided
2个回答

2

定义函数

window.requestAnimFrame()

这种方法只会在没有其他函数存在时使用setTimeout。它可以更明确地重写为:

window.requestAnimFrame = (function(callback){
    var theFunction;
    if (window.requestAnimationFrame) {
        theFunction = window.requestAnimationFrame;
    } else if (window.webkitRequestAnimationFrame) {
        theFunction = window.webkitRequestAnimationFrame;
    } else if (window.mozRequestAnimationFrame) {
        theFunction = window.mozRequestAnimationFrame;
    } else if (window.oRequestAnimationFrame) {
        theFunction = window.oRequestAnimationFrame;
    } else if (window.msRequestAnimationFrame) {
        theFunction = window.msRequestAnimationFrame;
    } else {
        theFunction = function(callback){
            window.setTimeout(callback, 100);
        };
    } 
    return theFunction;
})();

所以,只有在其他函数不存在时,它才使用超时。这个函数用于在旧浏览器上无法使用requestAnimationFrame的情况下借助专有实现进行回退。那些旧浏览器将使用超时。
根据定义,requestAnimationFrame的帧率由客户端确定。这意味着它可以每秒运行180次(就像在chrome中看到的那样),也可以每秒运行1次。它允许客户端浏览器确定什么最重要,并为其提供一种方法,在不必要时关闭屏幕刷新(例如当选项卡不活动或移动浏览器被最小化时)。

啊,那最好还是让它继续使用requestanimframe运行,并将ajax限制在每秒不超过10次。这样,如果他们最小化了或者其他什么情况,ajax就不会继续运行了。- 对于shmiddty,我确实感到有些愚蠢,当时没有好好考虑,只是想达到当时的目的,后来回头看时读错了。 - Noname Provided
1
@Shmiddty,也许我不需要,但效果与我所写的相同。至少在JavaScript中,||运算符仅在获得非false结果(false,“false”,0,“”,null,undefined都是false)之前进行评估。 - Patrick Gunderson
@NonameProvided,是的,请将您的ajax调用保持在requestAnimationFrame之外。 - Patrick Gunderson

1

http://jsfiddle.net/VMyn8/1/

我相信你的fps计时器是错误的。使用webkitRequestAnimationFrame,我得到了一个稳定的60FPS(实际上是大约62)。

var last = new Date(),
    frames = [],
    div = document.getElementById("fps");

(function loop(){
    var span = ((new Date()) - last)/1000;
    last = new Date();
    frames.push(span);

    if (frames.length > 100) frames.shift();

    var total = 0;    
    for (var i = 0; i < frames.length; i++)total += frames[i];    

    div.innerHTML = Math.floor(frames.length/total);
    //setTimeout(loop, 1000/60);
    webkitRequestAnimationFrame(loop);
})();

如果您想要轮询服务器,我建议使用完全不同的方法:
(function poll(){
    // do some stuff

    setTimeout(poll, 3000); // poll every 3 seconds
    // if you are doing an ajax call, you'll want to place the timeout in the 
    // callback so you aren't initiating another call before you've received 
    // a response to the current one. 
})();

我不知道为什么会得到3倍的意图,但重要的是获得相同的跨浏览器。 - Noname Provided
@NonameProvided 如果你想删除它们并查看结果是否相同,请随意这样做。 - Shmiddty
已经为你完成了,但是看到那段代码后我自己也感到尴尬。不过仔细阅读之后有所收获。现在我只需要想办法限制一下请求频率,以免对服务器/数据库造成垃圾负载。如果你们中有人好奇的话,我已经编辑了我的回复,最初它说的是“这是因为你在CSS中使用了-webkit-animation: bounce .5s ease-in-out infinite;”。是个新手错误,我犯了假设并在检查之前发布了回复。 - Noname Provided
是的,我已经改成那样了。我还编辑了上面的问题,因为我发现我得到的帧数是预期的3倍。 - Noname Provided

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