深入探寻javascript定时器
javascript单线程
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
队列任务
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
异步事件驱动
浏览器中很多行为是异步(Asynchronized)的,例如鼠标点击事件、窗口大小拖拉事件、定时器触发事件、XMLHttpRequest完成回调等。当一个异步事件发生的时候,它就进入事件队列。浏览器有一个内部大消息循环,Event Loop(事件循环),会轮询大的事件队列并处理事件。例如,浏览器当前正在忙于处理onclick事件,这时一个事件发生了(如window onSize),这个异步事件就被放入事件队列等待处理,只有前面的处理完毕了,空闲了才会执行这个事件。
Event Loop
JavaScript是单线程的,但浏览器不是单线程的
浏览器至少会有以下一些进程
1.浏览器 GUI 渲染线程
2.JavaScript 引擎线程
3.浏览器定时触发器线程
4.浏览器事件触发线程
5.浏览器 http 异步请求线程
因为 JavaScript 引擎是单线程的,所以代码都是先压到队列,然后由引擎采用先进先出的方式运行。事件处理函数、timer 执行函数也会排到这个队列中,然后利用一个无穷回圈,不断从队头取出函数执行,这个就是Event Loop。
一句话,js是单线程的,浏览器是多线程的,遇到异步的东西都是由浏览器把异步的回调放到Event Loop中,js线程不繁忙的时候,再去读取Event Loop
定时器原理
定时器的用法
setTimeout(fn, delay)
setInterval(fn, delay)
fn是函数也可以是字符串,delay是延迟的时间,单位是毫秒
有以下要注意的
1.fn虽然可以是字符串,一直都不推荐这么使用
2.fn里面的函数如果有this,执行的时候this会指向到window上面去
如果理解好了js单线程和Event loop,定时器的原理也很好理解了
如果设置了定时器,当到了延迟时间,浏览器会把延迟执行的事件放到Event loop里面,当时间到了,如果js线程空闲就会执行了(所以定时器的精度不准啊)
看有文章介绍说setTimeout和setInterval对函数一直轮询的区别,代码如下
setTimeout(function(){
setTimeout(arguments.callee,100)
},100)
setInterval(function(){},1000)
文章大概的意思是setTimeout是在回调函数执行后才启动下一次定时器,所以肯定是有间隔的执行,而setInterval是一直都在执行,如果碰到了js线程一直执行,可能就会在Event loop里面加多个回调,当js线程不忙的时候,会一下子执行多个
经过测试发现无论在ie,ff,chrome,Opera,Safari下,setInterval都是按一定间隔来的
测试代码如下
setInterval(function(){
xx.innerHTML=xx.innerHTML+1;
},100);
for(var i=0;i<6000000;i++){
xx.offsetWidth
}
setTimeout(function(){
debugger;
},10)
断点的时候还是只打印出了1个1
定时器的精度问题
因为js单线程原因,如果遇到繁忙,定时器肯定不准,而且肯定是越来越长的,这个好像无解啊,无解啊
还有一个精度问题就是最小间隔setTimeout(fun,0)
在js线程不忙的时候,也不可能0秒后马上执行,总有个最小间隔,每个浏览器还各不一样,这个未做测试
我看一篇文章中说的是w3c的标准,定时器的最小时间执行是4ms,找不到出处无从考证呀!!!
定时器相关的一些优化
在做定时器的时候还是可以有一些优化的
1.比如如果绑定window.onresize,在浏览器缩放的时候,该触发的非常频繁,所以可以延迟执行,当下次执行到的时候clear掉,减少频繁执行
伪代码如下
var timer;
function r(){
clearTimeout(timer);
timer = setTimeout(function(){
//do something
},150);
}
2.在滚动条往下拉的时候也是一下,比如图片的lazyload,也应该有一个定时器,避免过多的计算
3.当有多个地方需要定时器的时候,可以合并成一个定时器,时间间隔以最小的那个为准,然后需要执行的回调函数往数组里面塞,当到了时间间隔,遍历数组执行即可
一个小demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
.wrap{width:80%; margin: 30px auto; border: 1px solid #c; padding: 20px;}
.c{border: 1px solid #c; height: 30px;margin-bottom: 20px;}
</style>
</head>
<body>
<div id="xx"></div>
<div class="wrap" >
<div id="a1" class="c">0</div>
<div id="a2" class="c">0</div>
<div id="a3" class="c">0</div>
<div id="a4" class="c">0</div>
</div>
<script src=">
<script type="text/javascript">
var runTime = {
options : {
step : 1000
},
callbacks:[],
addCallbacks : [],
start : false,
timer : null,
extend : function(){
var target = arguments[0] || {}, i = 1, length = arguments.length, options;
if ( typeof target != "object" && typeof target != "function" )
target = {};
for ( ; i < length; i++ )
if ( (options = arguments[ i ]) != null )
for ( var name in options ) {
var copy = options[ name ];
if ( target === copy )
continue;
if ( copy !== undefined )
target[ name ] = copy;
}
return target;
},
init : function(options){
$.extend(this,this.options,options||{});
},
add : function(fun,options){
options = options ||{};
this.addCallbacks.push({
fun : fun,
startTime : new Date().getTime(),
step : options.step || this.step,
i : 1
});
var self = this;
if(!this.start){
this.callbacks = [fun];
this.start = true;
this.startTime = new Date().getTime();
this.timer = setInterval(function(){
self.done();
},this.step);
}
},
done : function(){
var callbacks = this.callbacks,
self = this,
newArr = [];
$.each(callbacks,function(i,obj){
if(obj.step == self.step){
obj.fun();
}else{
if(obj.i == obj.step/self.step){
if((new Date().getTime())-obj.startTime>obj.step2/3){
obj.fun();
}
obj.i = 1;
}else{
obj.i = obj.i + 1;
}
}
});
$.each(this.addCallbacks,function(i,obj){
if(obj.step == self.step){
if((new Date().getTime())-obj.startTime>obj.step2/3){
obj.fun();
callbacks.push(obj);
}else{
newArr.push(obj);
}
}else{
obj.i = obj.i + 1;
callbacks.push(obj);
}
});
this.addCallbacks = newArr;
},
clear : function(){
clearInterval(this.timer);
}
}
runTime.init();
runTime.add(function(){
a1.innerHTML = ~~a1.innerHTML+1;
});
runTime.add(function(){
a2.innerHTML = ~~a2.innerHTML+1;
},{step:2000});
runTime.add(function(){
a3.innerHTML = ~~a3.innerHTML+1;
},{step:4000});
runTime.add(function(){
a4.innerHTML = ~~a4.innerHTML+1;
},{step:8000});
</script>
</body>
</html>
小伙伴们是否对javascript定时器有所了解了呢,如有疑问给我留言吧。
编程语言
- 宿迁百度关键词排名指南:实现精准营销的关键
- 四川SEO优化怎么做网络推广
- 立昂技术备案老域名收购:如何为您的业务赋能
- 安徽百度关键词seo贵不贵,一般需要多少钱
- 吉林百度快照排名怎么做电话营销
- 多伦新手做SEO怎么做
- 甘肃优化关键词排名推广怎么做论坛营销
- 沙雅SEO网站推广:提升您的在线可见性
- 四川SEO优化如何提升销售额和销售量
- 聂荣网站排名优化:提升网站可见性的全方位指
- 涞水SEO:提升地方企业在线可见性的策略
- 辽宁百度seo排名怎样做网站排名
- 临湘哪有关键词排名优化:提升网站可见度的关
- 黑龙江百度网站优化有没有优惠
- 凉城优化关键词排名推广:提升您的网络可见性
- 萝北整站优化:提升您网站流量和排名的全面指