1、经过定时器咱们能够间隔设定时间重复调用某个函数,利用这个特性,咱们能够作不少事,例如,12306上的每间隔5秒查询自动查询一次余票,简单动画的实现等等html
2、定时器的格式:浏览器
定时器有两种格式,分别是setInterval(func, time) 和 setTimeout(func, time),这两个有一些区别函数
一、setInterval(func, tine);动画
(1)、 此定时器操做是这样的,解释到该语句时,是要间隔time时间后第一次执行func函数,间隔time时间后再次执行func函数,重复上述...spa
来个demo理解一下吧:线程
var count = 0; var print = function () { console.log(count++); } // 每间隔1秒调用一次print函数,因此带引结果应该是从0,1,2...一直打印 setInterval(print, 5000);
P.S. 咱们在setInterval()中传递函数时,咱们通常用个匿名函数包裹一下,而后再匿名函数中执行咱们要调用的函数,这样咱们能够在匿名函数中进行更多操做,更灵活一些,以下code
var count = 0; var print = function () { console.log(count++); } // 每间隔1秒调用一次print函数,因此带引结果应该是从0,1,2...一直打印 setInterval(function () { print(); }, 1000);
(2)、如何中止计时器?htm
setInterval有个返回值,咱们获取其返回值后,调用clearInterval(返回值), 便可中止计时器,以下demoblog
var count = 0; // 间隔1s打印一次cunt,当count为5时结束定时器,因此打印结果为 0, 1, 2, 3, 4 var timer = setInterval(function () { count === 5? clearInterval(timer) : console.log(count++); }, 1000);
二、setTimeout()递归
(1)、这个和上边那个定时器用法同样,都是传函数和时间间隔,但运行时操做是不一样,这个是间隔设定时间后,调用咱们传入的函数,而后就结束了,因此和上边那个区别在于上边那个是屡次,而这个只有一次。
// 间隔1s后带引setTimeout, 因此打印结果就是setTimeout setTimeout(function () { console.log('setTimeout'); }, 1000);
(2)、中止计时器,setTimeout()也有返回值,咱们获取返回值,而后执行clearTimeout(返回值),便可中止setTimeout()计时器.
(3)、利用setTimeout()来模拟setInterval()
原理很简单,就是递归,在函数内不断的中止计时器,再添加计时器,达到重复的目的,很少说,直接上demo:
var count = 0; var timer = null; function print() { // 每次添加定时器前,移除前一个定时器 clearTimeout(timer); //函数执行的语句 console.log(count++);
// 添加定时器
timer = setTimeout(function () {
print(); }, 1000);
// 循环结束条件 if(count == 10) { clearTimeout(timer); }
}
timer = setTimeout(function () { print(); }, 1000);
由代码咱们能够看出打印结果为0~9,
注意点:
i、咱们调用的函数内首先第一件事就是移除上一个定时器,由于上一个定时器已经没有做用了,因此要移除,而后再添加新的定时器
ii、在函数内最好在移除完定时器后紧跟着添加定时器,为何呢?觉得若是咱们遇到这样一种状况,定时器调用的函数须要执行很长一段时间,而后咱们在函数最后添加定时器,最终就形成了这样一种结果,那就是间隔时间变为 (函数执行时间 + 定时器间隔时间)极大的延迟了间隔时间,这确定不是咱们想要的。
那为何添加到函数前面就没这种状况了,由于在函数执行后首先就是移除上一个计时器,而后添加新的计时器,这时计时器就开始计时了,而函数正在主线程中被执行,通过计时器设定间隔时间后,将新添加的计时器函数添加到js执行队列中,等主线程的函数执行完毕后,主线程空闲,则直接执行js执行队列中等待的计时器函数。这就大大减小了函数执行时间的影响,由于定时器间隔计时和函数执行时间同时进行了,而不是函数执行完再进行计时器的间隔,这就使间隔时间尽可能的接近计时器设定的间隔时间。
3、计时器执行原理和单线程:
一、咱们都知道js语言是单线程的,那单线程是什么意思呢?那就是‘JavaScrip引擎是单线程运行的,浏览器不管何时都有且只有一个主线程在执行JavaScript程序’
二、js执行队列:因为js是运行在单线程环境的,因此线程一直是执行者同一个任务,而咱们是讲各类欲执行的任务随机添加到js执行上,当主线程空闲时就会当即执行当前队首的任务。
也能够这样理解,js执行队列是不少有着各自事务要办的人排成的长队,而线程是一个能够处理各类事务的窗口,可是该窗口一次只能办理一我的的事务,在窗口处理完当前人的事务后,窗口就处于空闲状态,而后接着办理长队队首人的事务,就这样按照长队顺序,窗口一次处理一我的的事务。而咱们所见的代码执行仅仅是将该代码事务随机插入到js执行队列中排队等待而已,但由于浏览器js引擎处理事务很是快,因此给咱们当即执行不用排队等待的感受。(注意:往js执行队列插入任务时是随机插入到队列中的,并不必定是要插入到队尾)
三、计时器工做方式:咱们添加一个定时器后,在间隔咱们设定时间后,将函数代码随机插入到js执行队列中,等主线程空闲时且该函数代码位于队首,才会执行函数代码。
四、总结来讲,就是在JavaScript中没有任何代码是当即执行的,只能说一旦主线程空闲则尽快执行
五、setInteral带来的问题:
(1)、假设使用重复定时器setInterval()定时器,而后添加的函数在线程须要执行很长时间,而重复定时器设定的间隔时间远远小于该函数执行时间,则在该函数执行期间,该定时器代码会被屡次添加到js执行队列等待,这就致使第一次函数执行结束后以后,以前屡次添加的函数代码会连续执行而没有间隔,而且第一个函数开始执行须要等主线程空闲才行,因此其第一次的间隔时间也不必定是咱们设定的。(由于在上一个函数执行时多个函数代码已经添加到js执行队列中,因此函数执行结束后会马上执行已经添加的函数代码)
(2)、鉴于上述问题,js引擎设定了这样一条规则:当使用setInterval()时,仅当js执行队列中没有该定时器的任何其余代码实例时,才能将该定时器代码添加到js执行队列中。不然就是加入失败,从新计时。
但此规则又带来了其余问题;
i、某些间隔会被跳过
ii、多个定时器代码执行之间的间隔可能会比预期的小,实际咱们等待间隔大于等于咱们设定的间隔时间。
(3)因此在使用setInterval()作动画时要注意两个问题:
i、不能使用固定步长做为作动画,必定要使用百分比: 开始值 + (目标值 - 开始值) * (Date.now() - 开始时间)/ 时间区间
ii、若是主进程运行时间过长,会出现跳帧的现象(由于js引擎的这条规则而没法添加函数代码)
(4)、解决:
使用链式setTimeout(),如上演示的用setTimeout()来模拟setInterval(),即每次在函数体执行结束的最后添加延时定时器,使函数执行之间的间隔最小为咱们设定的间隔时间。
第三项参考来源:http://www.cnblogs.com/dojo-lzz/p/4606448.html
------------------------------------------------------------------------------------------------------end