JS提供了一些原生的方法来实现延时执行某段代码。下面简单介绍一下setTiemout、setInterval、setImmediate、requestAnimationFrame。1.什么是定时器?JS提供了一些native方法来实现延迟执行某段代码。这里简单介绍一下setTimeout:设置一个定时器,定时器超时后执行一个函数或代码段vartimeoutId=window.setTimeout(func[,delay,param1,param2,...]);vartimeoutId=window.setTimeout(code[,delay]);timeoutId:timerIDfunc:delay后要执行的函数millisecond),默认值为0param1,param2:传递额外参数给delay函数,IE9及以上支持setInterval:以固定间隔重复调用一个函数或代码段varintervalId=window.setInterval(func,delay[,param1,param2,...]);varintervalId=window.setInterval(code,delay);intervalId:重复操作的IDfunc:延迟调用的函数code:代码段delay:延迟时间,无默认值Node0.10+),类似setTimeout(func,0)varimmediateId=setImmediate(func[,param1,param2,...]);varimmediateId=setImmediate(func);immediateId:timerIDfunc:callbackrequestAnimationFrame:API特别专为高性能帧动画而设计,但不能指定延迟时间,而是根据浏览器(frame)的刷新率varrequestId=window.requestAnimationFrame(func);func:callback上面简单介绍了四种JS定时器,本文主要介绍比较常用的两种类型:setTimeout和setInterval。2.举个栗子。基本用法//下面的代码执行后会输出什么?varintervalId,timeoutId;timeoutId=setTimeout(function(){console.log(1);},300);setTimeout(function(){clearTimeout(timeoutId);console.log(2);},100);setTimeout('console.log("5")',400);intervalId=setInterval(function(){console.log(4);clearInterval(intervalId);},200);//分别输出:2,4,5*setInterval和setTimeout有什么区别?//执行上面的代码块会输出什么?setTimeout(function(){console.log('timeout');},1000);setInterval(function(){console.log('interval')},1000);//输出超时,每1S间隔输出一次/--------------------------------///通过setTimeout模拟setInterval和setInterval有区别吗?varcallback=function(){if(times++>max){clearTimeout(timeoutId);clearInterval(intervalId);}console.log('开始',Date.now()-开始);for(vari=0;i<990000000;i++){}console.log('end',Date.now()-开始);},delay=100,times=0,max=5,start=Date.now(),intervalId,timeoutId;函数imitateInterval(fn,delay){timeoutId=setTimeout(function(){fn();if(times<=max){imitateInterval(fn,delay);}},delay);}imitateInterval(callback,delay);intervalId=setInterval(回调,延迟);如果是setTimeout和setInterval,它们只是执行次数不同,setTimeout一次,setIntervaln次setInterval和setTimeout模拟的setInterval的区别在于,setTimeout只有在回调完成后才会调用下一个定时器,而setInterval会在指定时间到达时不管回调函数是否执行,都会在事件队列中插入一个定时器。回调事件是执行的,所以在选择定时器方法的时候需要考虑setInterval这个特性对你的业务代码有没有影响?*哪个更快,setTimeout(func,0)或setImmediate(func)?(出于好奇,我只是写了这个测试))=>{console.timeEnd('超时');},0);在Node.JSv6.7.0测试,发现setTimeout执行的更早*面试题中运行如下代码的结果是什么?//主题1vart=true;setTimeout(function(){t=false;},1000);while(t){}alert('end');/------------------------------///主题2for(vari=0;i<5;i++){setTimeout(function(){console.log(i);},0);}/--------------------------------///主题3varobj={msg:'obj',shout:function(){alert(this.msg);},waitAndShout:function(){setTimeout(function(){this.shout();},0);}};obj.waitAndShout();问题的答案后面会有解答##三、JS定时器的工作原理在解释上面问题的答案之前,我们先来了解一下定时器的工作原理。这里我们将使用参考HowJavaScriptTimersWork中的例子讲解定时器的工作原理,这张图是简化版的原理图!orient/strip%7CimageView2/2/w/1240)上图中左边的数字代表时间,单位是毫秒;左边的文字代表一个操作完成后,浏览器会询问当前队列中有哪些操作在等待执行;蓝色方块表示正在执行的代码块;右边的文字表示代码运行过程中发生了哪些异步事件。该图的大致流程如下:*程序启动时,一段JS代码块开始执行,执行时间约为18ms。执行过程中,触发了3个异步事件,包括一个setTimeout,鼠标点击事件,和setInterval*,第一个setTimeout先运行,延时10ms,鼠标事件后出现,浏览器插入点击回调函数在事件队列,然后setInterval运行,10ms到达后,setTimeout将setTimeout回调插入事件队列*,当第一个代码块执行完成后,浏览器检查队列中有哪些事件在等待,它取出队列顶部的代码执行*当浏览器处理鼠标点击回调时,setInterval再次检查到达延迟时间,他将发送到事件队列中的一个间隔回调插入,并且一个回调每经过指定的延迟时间就会被插入到队列中*之后,浏览器执行完当前队头的代码后,会再次取出当前队头的事件toexecute这里只是对定时器的原理做一个简单的说明。实际的处理过程要比这复杂的多。##四、题答得好,我们现在来看上面面试题的答案。第一个问题>alert永远不会执行,因为js是单线程的,等待当前正在执行的任务完成后才会执行timer的回调,while(t){}直接进入死循环占用线程,不给回调函数执行的机会问题2>代码会输出5555,原因同上,当i=0时,生成定时器,将回调插入事件队列,并waitforcurrentqueuenotasks执行时立即执行,而此时for循环正在执行,所以回调被搁置。for循环执行时,队列中有5个回调函数,都会执行console.log(i)操作,因为目前的JS代码没有使用块级作用域,所以i的值为在for循环结束后,会一直为5,所以代码会输出55问题3>本题涉及this的指向。setTimeout()调用的代码运行在与其所在函数完全分离的执行环境中。这将导致这些代码中包含的this关键字将指向窗口(或全局)对象。window对象中不存在shout方法,所以会报错。修改如下:varobj={msg:'obj',shout:function(){alert(this.msg);},waitAndShout:function(){varself=this;//在这里将其分配给一个变量setTimeout(function(){self.shout();},0);}};obj.waitAndShout();##5.注意点*setTimeout有最小时间间隔限制,HTML5标准是4ms,小于4ms按4ms处理,但是每个浏览器实现的最小间隔不一样*因为JS引擎只有一个线程,所以会强制异步事件排队执行*如果setInterval的回调执行时间长于指定的delay,setInterval会一个接一个的执行,没有间隔*这个的指向问题可以通过bind函数,变量定义和箭头功能
