深入探寻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定时器有所了解了呢,如有疑问给我留言吧。
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程