setTimeout多浏览器,更快的异步执行
分类:关于美高梅

如果要异步执行一个函数,我们最先想到的方法肯定会是setTimeout
例如:setTimeout(function( /* 1s后做点什么 */){},1000}

更快的异步执行(setTimeout多浏览器),异步settimeout

如果要异步执行一个函数,我们最先想到的方法肯定会是setTimeout
例如:setTimeout(function( /* 1s后做点什么 */){},1000}

那如果说要最快速地异步执行一个函数呢?
是否会是:

setTimeout(function( /* 尽快做点什么 */){},0}

可惜的是,浏览器为了避免setTimeout嵌套可能出现卡死ui线程的情况,为setTimeout设置了最小的执行时间间隔,不同浏览器的最小执行时间间隔都不一样。chrome下测试 setTimeout 0 的实际执行时间间隔大概在12ms左右。

那么如果想最快地异步执行一个函数,有没有什么可以提速的方法呢?

先来看看浏览器端,有哪些常用的异步执行方法

setImmediate:该方法去实现比setTimeout 0 更快的异步执行,执行时间更接近0ms,但是只有IE/node支持。

requestAnimationFrame:做动画循环的时候经常会用到这个方法,该方法只会在浏览器刷新ui的时候执行,刷新ui的频率最大一般为60fps,所以requestAnimationFrame一般情况下比setTimeout 0 更慢一些。

除了使用异步函数外,还有一些方法可以实现异步调用

利用onmessage:
和iframe通信时常常会使用到onmessage方法,但是如果同一个window postMessage给自身,会怎样呢?其实也相当于异步执行了一个function
例如:

var doSth = function(){}
window.addEventListener("message", doSth, true);
window.postMessage("", "*");

``

另外,还可以利用script标签,实现函数异步执行,例如:
``

var newScript = document.createElement("script");
newScript.onreadystatechange = doSth;
document.documentElement.appendChild(newScript);

``
把script添加到文档也会执行onreadystatechange 但是该方法只能在IE下浏览器里使用。

那么 这几种方法,谁最快?

测试了一下,

chrome下:

setImmediate:不可用。
setTimeout 0:12ms
onmessage:6ms
onreadystatechange:不支持

chrome下,onmessage比setTimeout 0 更快。

firefox下:

setImmediate:不可用。
setTimeout 0:7ms
onmessage:7ms
onreadystatechange:不支持

firefox下,onmessage和setTimeout 0 速度相当。

IE9:

setImmediate:不可用。
setTimeout 0:11ms
onmessage:7ms 10ms
onreadystatechange:2ms

IE9下,onreadystatechange的时间比另外两者要快得多。

总体情况下,setImmediate < readystatechange < onmessage < setTimeout 0 < requestAnimationFrame
因此我们可以简单封装一个快速执行异步function的方法:

var setZeroTimeout = (function(){
if(window.setImmediate){
//IE10+版本,使用原生setImmediate
return window.setImmediate;
}
else if("onreadystatechange" in document.createElement("script")){
return function(){/* 使用onreadystatechange的版本 */}
}
else if(window.postMessage){
return function(){/* 使用onmessage的异步执行版本 */}
}
else {
return window.setTimeout;
}

})();

从JS执行机制说起

浏览器(或者说JS引擎)执行JS的机制是基于事件循环。

由于JS是单线程,所以同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行。

为了避免因为某些长时间任务造成的无意义等待,JS引入了异步的概念,用另一个线程来管理异步任务。

同步任务直接在主线程队列中顺序执行,而异步任务会进入另一个任务队列,不会阻塞主线程。等到主线程队列空了(执行完了)的时候,就会去异步队列查询是否有可执行的异步任务了(异步任务通常进入异步队列之后还要等一些条件才能执行,如ajax请求、文件读写),如果某个异步任务可以执行了便加入主线程队列,以此循环。

使用requestAnimationFrame实现js动画性能好。先给大家简单介绍下requestAnimationFrame比起setTimeout、setInterval有哪些优势?

那如果说要最快速地异步执行一个函数呢?
是否会是:

怎让settimeout不等待时间立即执行

把5000改成0不就行了么.不知道你要用在什么地方.? 要实现什么功能.?

实在不行你加我好友.QQ:953954891 直接QQ回答你问题.这样方便一点.  

JS定时器

示例一:

setTimeout(function( /* 尽快做点什么 */){},0}

JavaScript 多个settimeout的问题

你把两个的时间岔开试一下,white效果被或一个效果覆盖,所以看不到white效果

试着 执行以下代买看看:
<div style="border:1px solid red ;background-color:black;"><div><div id="dd" >s</div></div></div>
<SCRIPT LANGUAGE="JavaScript">
<!--
function changeColor(obj)
{

var row=obj.parentElement.parentElement;

var ss=row.style.backgroundColor;

row.style.backgroundColor = 'Yellow';

window.setTimeout(function() { row.style.backgroundColor = 'White';},200);
window.setTimeout(function() { row.style.backgroundColor = ss;},500);

}

changeColor(dd);
//-->
</SCRIPT>  

如果要异步执行一个函数,我们最先想到的方法肯定会是setTimeout 例如:setTimeout(function(...

JS的定时器目前有三个:setTimeout、setInterval和setImmediate。

requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:
1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
像setTimeout、setInterval一样,requestAnimationFrame是一个全局函数。调用requestAnimationFrame后,它会要求浏览器根据自己的频率进行一次重绘,它接收一个回调函数作为参数,在即将开始的浏览器重绘时,会调用这个函数,并会给这个函数传入调用回调函数时的时间作为参数。由于requestAnimationFrame的功效只是一次性的,所以若想达到动画效果,则必须连续不断的调用requestAnimationFrame,就像我们使用setTimeout来实现动画所做的那样。requestAnimationFrame函数会返回一个资源标识符,可以把它作为参数传入cancelAnimationFrame函数来取消requestAnimationFrame的回调。怎么样,是不是也跟setTimeout的clearTimeout很相似啊。
所以,可以这么说,requestAnimationFrame就是一个性能优化版、专为动画量身打造的setTimeout,不同的是requestAnimationFrame不是自己指定回调函数运行的时间,而是跟着浏览器内建的刷新频率来执行回调,这当然就能达到浏览器所能实现动画的最佳效果了。
目前,各个支持requestAnimationFrame的浏览器有些还是自己的私有实现,所以必须加前缀,对于不支持requestAnimationFrame的浏览器,我们只能使用setTimeout,因为两者的使用方式几近相同,所以这两者的兼容并不难。对于支持requestAnimationFrame的浏览器,我们使用requestAnimationFrame,而不支持的我们优雅降级使用传统的setTimeout。把它们封装一下,就能得到一个统一兼容各大浏览器的API了。
代码可以到这里来查看:

可惜的是,浏览器为了避免setTimeout嵌套可能出现卡死ui线程的情况,为setTimeout设置了最小的执行时间间隔,不同浏览器的最小执行时间间隔都不一样。chrome下测试 setTimeout 0 的实际执行时间间隔大概在12ms左右。

定时器也是一种异步任务,通常浏览器都有一个独立的定时器模块,定时器的延迟时间就由定时器模块来管理,当某个定时器到了可执行状态,就会被加入主线程队列。

JS定时器非常实用,做动画的肯定都用到过,也是最常用的异步模型之一。

有时候一些奇奇怪怪的问题,加一个setTimeout(fn, 0)(以下简写setTimeout(0))就解决了。不过,如果对定时器本身不熟悉,也会产生一些奇奇怪怪的问题。

setTimeout

setTimeout(fn, x)表示延迟x毫秒之后执行fn。

使用的时候千万不要太相信预期,延迟的时间严格来说总是大于x毫秒的,至于大多少就要看当时JS的执行情况了。

另外,多个定时器如不及时清除(clearTimeout),会存在干扰,使延迟时间更加捉摸不透。所以,不管定时器有没有执行完,及时清除已经不需要的定时器是个好习惯。

HTML5规范规定最小延迟时间不能小于4ms,即x如果小于4,会被当做4来处理。 不过不同浏览器的实现不一样,比如,Chrome可以设置1ms,IE11/Edge是4ms。

setTimeout注册的函数fn会交给浏览器的定时器模块来管理,延迟时间到了就将fn加入主进程执行队列,如果队列前面还有没有执行完的代码,则又需要花一点时间等待才能执行到fn,所以实际的延迟时间会比设置的长。如在fn之前正好有一个超级大循环,那延迟时间就不是一丁点了。

1

2

3

4

5

6

7

8(functiontestSetTimeout(){

constlabel='setTimeout';

console.time(label);

setTimeout(()=>{

console.timeEnd(label);

},10);

for(leti=0;i<100000000;i++){}

})();

结果是:setTimeout: 335.187ms,远远不止10ms。

setInterval

setInterval的实现机制跟setTimeout类似,只不过setInterval是重复执行的。

对于setInterval(fn, 100)容易产生一个误区:并不是上一次fn执行完了之后再过100ms才开始执行下一次fn。 事实上,setInterval并不管上一次fn的执行结果,而是每隔100ms就将fn放入主线程队列,而两次fn之间具体间隔多久就不一定了,跟setTimeout实际延迟时间类似,和JS执行情况有关。

1

2

3

4

5

6

7

8

9

10

11(functiontestSetInterval(){

leti=0;

conststart=Date.now();

consttimer=setInterval(()=>{

i+=1;

i===5&&clearInterval(timer);

console.log(`第${i}次开始`,Date.now()-start);

for(leti=0;i<100000000;i++){}

console.log(`第${i}次结束`,Date.now()-start);

},100);

})();

输出

1

2

3

4

5

6

7

8

9

10第1次开始100

第1次结束1089

第2次开始1091

第2次结束1396

第3次开始1396

第3次结束1701

第4次开始1701

第4次结束2004

第5次开始2004

第5次结束2307

可见,虽然每次fn执行时间都很长,但下一次并不是等上一次执行完了再过100ms才开始执行的,实际上早就已经等在队列里了。

另外可以看出,当setInterval的回调函数执行时间超过了延迟时间,已经完全看不出有时间间隔了。

如果setTimeout和setInterval都在延迟100ms之后执行,那么谁先注册谁就先执行回调函数。

setImmediate

这算一个比较新的定时器,目前IE11/Edge支持、Nodejs支持,Chrome不支持,其他浏览器未测试。

从API名字来看很容易联想到setTimeout(0),不过setImmediate应该算是setTimeout(0)的替代版。

在IE11/Edge中,setImmediate延迟可以在1ms以内,而setTimeout有最低4ms的延迟,所以setImmediate比setTimeout(0)更早执行回调函数。不过在Nodejs中,两者谁先执行都有可能,原因是Nodejs的事件循环和浏览器的略有差异。

1

2

3

4

5

6

7

8(functiontestSetImmediate(){

constlabel='setImmediate';

console.time(label);

setImmediate(()=>{

console.timeEnd(label);

});

})();

Edge输出:setImmediate: 0.555 毫秒

很明显,setImmediate设计来是为保证让代码在下一次事件循环执行,以前setTimeout(0)这种不可靠的方式可以丢掉了。

其他常用异步模型

requestAnimationFrame

requestAnimationFrame并不是定时器,但和setTimeout很相似,在没有requestAnimationFrame的浏览器一般都是用setTimeout模拟。

requestAnimationFrame跟屏幕刷新同步,大多数屏幕的刷新频率都是60Hz,对应的requestAnimationFrame大概每隔16.7ms触发一次,如果屏幕刷新频率更高,requestAnimationFrame也会更快触发。基于这点,在支持requestAnimationFrame的浏览器还使用setTimeout做动画显然是不明智的。

在不支持requestAnimationFrame的浏览器,如果使用setTimeout/setInterval来做动画,最佳延迟时间也是16.7ms。 如果太小,很可能连续两次或者多次修改dom才一次屏幕刷新,这样就会丢帧,动画就会卡;如果太大,显而易见也会有卡顿的感觉。

有趣的是,第一次触发requestAnimationFrame的时机在不同浏览器也存在差异,Edge中,大概16.7ms之后触发,而Chrome则立即触发,跟setImmediate差不多。按理说Edge的实现似乎更符合常理。

1

2

3

4

5

6

7

8(functiontestRequestAnimationFrame(){

constlabel='requestAnimationFrame';

console.time(label);

requestAnimationFrame(()=>{

console.timeEnd(label);

});

})();

Edge输出:requestAnimationFrame: 16.66 毫秒

Chrome输出:requestAnimationFrame: 0.698ms

但相邻两次requestAnimationFrame的时间间隔大概都是16.7ms,这一点是一致的。当然也不是绝对的,如果页面本身性能就比较低,相隔的时间可能会变大,这就意味着页面达不到60fps。

Promise

Promise是很常用的一种异步模型,如果我们想让代码在下一个事件循环执行,可以选择使用setTimeout(0)、setImmediate、requestAnimationFrame(Chrome)和Promise。

而且Promise的延迟比setImmediate更低,意味着Promise比setImmediate先执行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21functiontestSetImmediate(){

constlabel='setImmediate';

console.time(label);

setImmediate(()=>{

console.timeEnd(label);

});

}

functiontestPromise(){

constlabel='Promise';

console.time(label);

newPromise((resolve,reject)=>{

resolve();

}).then(()=>{

console.timeEnd(label);

});

}

testSetImmediate();

testPromise();

Edge输出:Promise: 0.33 毫秒 setImmediate: 1.66 毫秒

尽管setImmediate的回调函数比Promise先注册,但还是Promise先执行。

可以肯定的是,在各JS环境中,Promise都是最先执行的,setTimeout(0)、setImmediate和requestAnimationFrame顺序不确定。

process.nextTick

process.nextTick是Nodejs的API,比Promise更早执行。

事实上,process.nextTick是不会进入异步队列的,而是直接在主线程队列尾强插一个任务,虽然不会阻塞主线程,但是会阻塞异步任务的执行,如果有嵌套的process.nextTick,那异步任务就永远没机会被执行到了。

使用的时候要格外小心,除非你的代码明确要在本次事件循环结束之前执行,否则使用setImmediate或者Promise更保险。

var lastTime = 0;
var prefixes = 'webkit moz ms o'.split(' '); //各浏览器前缀
var requestAnimationFrame = window.requestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame;
var prefix;
//通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
for( var i = 0; i < prefixes.length; i++ ) {
 if ( requestAnimationFrame && cancelAnimationFrame ) {
 break;
 }
 prefix = prefixes[i];
 requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
 cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || window[ prefix + 'CancelRequestAnimationFrame' ];
}
//如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
if ( !requestAnimationFrame || !cancelAnimationFrame ) {
 requestAnimationFrame = function( callback, element ) {
 var currTime = new Date().getTime();
 //为了使setTimteout的尽可能的接近每秒60帧的效果
 var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); 
 var id = window.setTimeout( function() {
 callback( currTime + timeToCall );
 }, timeToCall );
 lastTime = currTime + timeToCall;
 return id;
 };
 cancelAnimationFrame = function( id ) {
 window.clearTimeout( id );
 };
}
//得到兼容各浏览器的API
window.requestAnimationFrame = requestAnimationFrame; 
window.cancelAnimationFrame = cancelAnimationFrame;

那么如果想最快地异步执行一个函数,有没有什么可以提速的方法呢?

这样子我们就能在所有浏览器上使用requestAnimationFrame和cancelAnimationFrame了。
下面举个简单的例子来说明怎么运用requestAnimationFrame进行动画,下面的代码会将id为demo的div以动画的形式向右移动到300px

先来看看浏览器端,有哪些常用的异步执行方法

<div id="demo" style="position:absolute; width:100px; height:100px; background:#ccc; left:0; top:0;"></div>
<script>
var demo = document.getElementById('demo');
function rander(){
 demo.style.left = parseInt(demo.style.left) + 1 + 'px'; //每一帧向右移动1px
}
requestAnimationFrame(function(){
 rander();
 //当超过300px后才停止
 if(parseInt(demo.style.left)<=300) requestAnimationFrame(arguments.callee);
});
</script>

setImmediate:该方法去实现比setTimeout 0 更快的异步执行,执行时间更接近0ms,但是只有IE/node支持。

示例二:

requestAnimationFrame:做动画循环的时候经常会用到这个方法,该方法只会在浏览器刷新ui的时候执行,刷新ui的频率最大一般为60fps,所以requestAnimationFrame一般情况下比setTimeout 0 更慢一些。

一直以来,JavaScript的动画都是通过定时器和间隔来实现的。虽然使用CSS transitions 和 animations使Web开发实现动画更加方便,但多年来以JavaScript为基础来实现动画却很少有所改变。直到Firefox 4的发布,才带来了第一种对JavaScript动画的改善的方法。但要充分认识改善,这有利于帮助我们了解web动画是如何演变改进的。
定时器Timer
用于创建动画的第一个模式是使用链式setTimeout()调用。在Netscape 3′s hayday的很长一段时期,开发者都记得一种在网络上随处可见的固定式最新行情状态栏,通常它类似于这样:

除了使用异步函数外,还有一些方法可以实现异步调用

(function(){ 
 var msg = "新的广告", 
 len = 25, 
 pos = 0, 
 padding = msg.replace(/./g, " ").substr(0,len), 
 finalMsg = padding + msg; 
 function updateText(){ 
 var curMsg = finalMsg.substr(pos++, len); 
 window.status = curMsg; 
 if (pos == finalMsg.length){ pos = 0; } 
 setTimeout(updateText, 100); 
 } 
 setTimeout(updateText, 100); 
})(); 

利用onmessage:
和iframe通信时常常会使用到onmessage方法,但是如果同一个window postMessage给自身,会怎样呢?其实也相当于异步执行了一个function
例如:

 如果你想在浏览器中测试这段代码,你可以新建一个
标签用来模拟window.status,例如:newsticker example
这种让人烦恼的web模式,后来遭到对window.status禁用的抵抗,但随着Explorer 4和Netscape 4的发布,浏览器第一次给开发者更多对页面元素的控制权限,这种技术再次出现。这样就出现了使用javascript动态改变元素大小、位置、颜色等的一种全新动画模式。例如,下面就是一个将div宽度变化成100%的动画(类似于进度条):

var doSth = function(){}
window.addEventListener("message", doSth, true);
window.postMessage("", "*");
(function(){ 
 function updateProgress(){ 
 var div = document.getElementByIdx_x("status"); 
 div.style.width = (parseInt(div.style.width, 10) + 5) + "%"; 
 if (div.style.width != "100%"){ setTimeout(updateProgress, 100); } 
 } 
 setTimeout(updateProgress, 100); 
})(); 

尽管动画在页面上的地方不同,但基本原理却是一样的:做出改变,用setTimeout()间隔使页面更新,然后setTimeout又执行下一次变化,这个过程反复执行,直到动画完成(见进度条动画),早期的状态栏动画是相同的技术,只是动画不一样而已。
间隔动画Intervals
随着成功将动画引入web,新的探索开始了。一个动画已经无法满足了,现在需要多个动画。首次尝试为每个动画创建多个动画循环,在早期的浏览器中使用setTimeout()来创建多个动画是有点复杂的,所以开发商开始使用setInterval()一创建单一的动画循环,来管理页面上所有的动画,一个使用wetInterval()的基本动画像这样:

另外,还可以利用script标签,实现函数异步执行,例如:

(function(){ 
 function updateAnimations(){ 
 updateText(); 
 updateProgress(); 
 } 
 setInterval(updateAnimations, 100); 
})();
var newScript = document.createElement("script");
newScript.onreadystatechange = doSth;
document.documentElement.appendChild(newScript);

创建一个小动画库,updateAnimations()方法将每一个动画(同时看到一个新闻股票和进度条在一起运行)循环执行并进行适当的改变。如果没有动画需要更新,该方法可以退出而不做任何事情,甚至停止动画循环,直到有更多的动画更新做好准备。
动画问题比较棘手的问题是延迟应该为多少。间隔一方面必须足够短,从而使不同的动画都能流畅的进行,别一方面还要足够长,使得浏览器可以完成渲染。大多数浏览器的刷新频率为60HZ,即每秒60次刷新,大多数浏览器的刷新频率都不会比这个更频繁,因为他们知道,最终用户是得不到更好的体验的。
鉴于此,为流畅动画的最佳时间间隔为1000毫秒/ 60,约17ms。在这个频率你会看到流畅的动画,那是因为你最大的接近了浏览器能达到的频率。跟以前的动画相比,你会发现17ms间隔的动画更加平滑,也更快(因为动画更新更频繁,没有做其他任何修改的情况下),多个动画可能需要节流,以免17ms的动画完成得太快。
问题
即使使用setInterval()为基础的动画循环比多套使用setTimeout()的动画循环高效,这里还是存在问题。无论是setInterval()还是setTimeout()都无法达到精确,这个延迟即你指定的第二个参数仅仅表示何时代码会添加到浏览器的可能被执行的UI线程队列中。如果队列中有其他工作在此之前,那代码将会等到他完成才会执行。简而言之,毫秒级的延迟不是表示何时代码会执行,而是表示何时代码会添加进队列。如果UI线程处于繁忙状态或在处理用户动作,那么代码将不会被马上执行。
美高梅网址,平滑动画的关键是理解下一帧何时被执行,直到现在都没有一个方法来保证下一帧将会在浏览器中被绘制。随着的日益流行和新的基于浏览器的游戏的出现,开发商对setInterval()和setTimeout()的不精准越来越感到失望。
浏览器的计时器分辨率加剧了这个问题,计时器对毫秒不精准,这里有一些常见的计时器分辨率:
Internet Explorer 8 and earlier 15.625ms
Internet Explorer 9 and later 4ms.
Firefox and Safari ~10ms.
Chrome has a timer 4ms.

IE在版本9之前的的分辨率为15.625,所以0~15之间的任意值可能是0或15,但没有分别。IE9的计时器分辨率改进为4ms,但涉及到动画时也是不具体的,chrome的计时器分辨率为4ms,firefox 和 safari的为10ms。因此即使你把间隔设定为最佳的显示效果,你也仅仅是得到这个近似值。
mozRequestAnimationFrame
Mozilla 的 Robert O'Callahan 在思考这个问题,并想出了一个独特的方案。他指出CSS transitions 和 animations的优势在于浏览器知道哪些动画将会发生,所以得到正确的间隔来刷新UI。而javascript动画,浏览器不知道动画正在发生。他的解决方案是创建一个mozRequestAnimationFrame()方法来告诉浏览器哪些javascript代码正在执行,这使得浏览在执行一些代码后得到优化。
mozRequestAnimationFrame()方法接受一个参数,是一个屏幕重绘前被调用的函数。这个函数用来对生成下合适的dom样式的改变,这些改变用在下一次重绘中。你可以像调用setTimeout()一样的方式链式调用mozRequestAnimationFrame(),例如:


把script添加到文档也会执行onreadystatechange 但是该方法只能在IE下浏览器里使用。

function updateProgress(){ 
 var div = document.getElementByIdx_x("status"); 
 div.style.width = (parseInt(div.style.width, 10) + 5) + "%"; 
 if (div.style.left != "100%"){ 
 mozRequestAnimationFrame(updateProgress); 
 } 
} 

那么 这几种方法,谁最快?

mozRequestAnimationFrame(updateProgress); 
由于mozRequestAnimationFrame()只运行给定的函数一次,你需要在下一次UI动画的时候再次调用它。你也需要相同的方法来管理何时停止调用。很酷,是非常流畅的动画增强的实例。
因此,mozRequestAnimationFrame()解决了浏览器不知道Javascript动画正在执行和不知道多少才是合适的间隔的问题,但对于不知道何时你的代码才被真正执行,也是由这个方案来解决的。
传递给mozRequestAnimationFrame()的函数实际是一个下一次重绘何时发生的的时间码(以毫秒为单位自1970年1月1日计算)。这是很重要的一点:mozRequestAnimationFrame()实际上列表出将要重绘的点并可以告诉你他们所处的时间。这样你就能够决定怎样更好的来调整你的动画。
为了得到上次重绘过去的时间,你可以查询mozAnimationStartTime,其中包含了过去重绘的时间代码。减去传递回调时的这个值可以计算出下一次重绘到屏幕时所用的时间。使用这些值的典型模式如下:

测试了一下,

function draw(timestamp){ 
 //calculate difference since last repaint 
 var diff = timestamp - startTime; 
 //use diff to determine correct next step 
 //reset startTime to this repaint 
 startTime = timestamp; 
 //draw again 
 mozRequestAnimationFrame(draw); 
} 
var startTime = mozAnimationStartTime; 
mozRequestAnimationFrame(draw); 

chrome下:

关键是第一次不是通过callback调用时,mozAnimationStartTime是到mozRequestAnimationFrame()经过的时间。如果是在回调函数中,mozAnimationStartTime是通过参数传递进来的时间代码平均值。
webkitRequestAnimationFrame
在很多人热忠于chrome时,随即创建了webkitRequestAnimationFrame()方法。这个版本与firefox的版本在两方面有着细微的差别。一方面,它不通过回调函数传递时间代码,你将无法知道下次重绘何时发生,另一方面,它添加了第二个可选参数来确定哪一个DOM元素发生改变。因此,如果你知道重绘发生在页面哪个部分的元素内,你可以限制重绘发生的区域。
应该不会感到惊讶,有没有相应的mozAnimationStartTime,因为如果没有下一个重绘的时间信息不是很有益。有,只是webkitCancelAnimationFrame()取消了之前计划的重绘。
如果你不需要精确的时间差异,你可以用下面的方式来创建一个用于Firefox4和chrome10+的动画:

setImmediate:不可用。
setTimeout 0:12ms
onmessage:6ms
onreadystatechange:不支持

function draw(timestamp){ 
 //calculate difference since last repaint 
 var drawStart = (timestamp || Date.now()), 
 diff = drawStart - startTime; 
 //use diff to determine correct next step 
 //reset startTime to this repaint 
 startTime = drawStart; 
 //draw again 
 requestAnimationFrame(draw); 
} 
var requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame, 
startTime = window.mozAnimationStartTime || Date.now(); 
requestAnimationFrame(draw); 

chrome下,onmessage比setTimeout 0 更快。

这种模式使用可用的方法来创建以花费多少时间为理念的循环动画。Firefox使用时间代码信息是有用的,而Chrome默认为欠精准的时间对象。当用这种模式的时候,时间的差异给你一种多少时间过去了的想法,但不会告诉你Chrome的下一次重绘出现在何时。不过这比只有多少时间过去了的模糊概念要好些。
总结
mozRequestAnimationFrame()方法的介绍为推动Javascript 动画及web的历史发展有着非常重要的作用。如前所述,JavaScript动画的态几乎和JavaScript的初期一样。随着浏览器逐渐推出CSS transitions 和 animations,很高兴看到基于JavaScript的动画的关注,因为这些在基于的游戏领域将变得更重要和更与CUP联系紧密。知道Javascript何时尝试动画,允许浏览器做更多的优化处理,包括在tab处于后台或移动设备电量过低时停止进程。
该requestAnimationFrame()API现在正由W3C起草一个新议案,并正由Mozilla和Google努力使之成为Web大舞台的一部分。很高兴能看到这两大集团这么迅速的兼容(可能不完全)实现。
RequestAnimFrame使用
对于一个侦中对DOM的所有操作,只进行一次Layout和Paint。
如果发生动画的元素被隐藏了,那么就不再去Paint。

firefox下:

window.requestAnimFrame = (function(){ 
 return window.requestAnimationFrame || 
 window.webkitRequestAnimationFrame || 
 window.mozRequestAnimationFrame || 
 window.oRequestAnimationFrame || 
 window.msRequestAnimationFrame || 
 function( callback ){ 
  window.setTimeout(callback, 1000/60); 
 }; 
})(); 
//调用 
function animationLoop(elem){ 
 requestAnimFrame(animationLoop); 
//logic 
} 
Or
window.requestAnimFrame = (function(w, r) { 
 w['r'+r] = w['r'+r] || w['webkitR'+r] || w['mozR'+r] || w['msR'+r] || w['oR'+r] || function(c){ w.setTimeout(c, 1000 / 60); }; 
 return w['r'+r]; 
})(window, 'equestAnimationFrame'); 

setImmediate:不可用。
setTimeout 0:7ms
onmessage:7ms
onreadystatechange:不支持

以上通过两段代码示例详解说明了使用requestAnimationFrame实现js动画性能好,希望大家喜欢。

firefox下,onmessage和setTimeout 0 速度相当。

您可能感兴趣的文章:

  • 缓动函数requestAnimationFrame 更好的实现浏览器经动画
  • window.requestAnimationFrame是什么意思,怎么用
  • BOM系列第二篇之定时器requestAnimationFrame
  • JS动画效果代码3
  • 用js实现的模拟jquery的animate自定义动画(2.5K)
  • 在AngularJS应用中实现一些动画效果的代码
  • JS判断页面加载状态以及添加遮罩和缓冲动画的代码
  • js动画(animate)简单引擎代码示例
  • 深入理解requestAnimationFrame的动画循环

IE9:

setImmediate:不可用。
setTimeout 0:11ms
onmessage:7ms 10ms
onreadystatechange:2ms

IE9下,onreadystatechange的时间比另外两者要快得多。

总体情况下,setImmediate < readystatechange < onmessage < setTimeout 0 < requestAnimationFrame
因此我们可以简单封装一个快速执行异步function的方法:

var setZeroTimeout = (function(){
if(window.setImmediate){
//IE10+版本,使用原生setImmediate
return window.setImmediate;
}
else if("onreadystatechange" in document.createElement("script")){
return function(){/* 使用onreadystatechange的版本 */}
}
else if(window.postMessage){
return function(){/* 使用onmessage的异步执行版本 */}
}
else {
return window.setTimeout;
}

})();

本文由美高梅网址发布于关于美高梅,转载请注明出处:setTimeout多浏览器,更快的异步执行

上一篇:一个超级简单的方法判断浏览器的内核前缀,j 下一篇:没有了
猜你喜欢
热门排行
精彩图文