人们对 JavaScript的定时器存在广泛的误解,认为它们是线程,其实 JavaScript 是运行于单线程的环境中的,而定时器仅仅只是计划代码在将来的某个时间执行。执行时机是不能保证的,由于在页面的生命周期中,不一样时间可能有其余代码在控制JavaScript进程。在页面下载完后的代码运行、事件处理程序、Ajax回调函数都必须使用一样的线程来执行。实际上,浏览器负责进行排序,指派某段代码在某个时间点运行 的优先级。java
定时器对队列的工做方式是,当特定时间过去后将代码插入。注意,给队列添加代码并不意味着对它马上执行,而只能表示它会尽快执行。设定一个 150ms 后执行的定时器不表明到了 150ms 代码就马上 执行,它表示代码会在150ms后被加入到队列中。若是在这个时间点上,队列中没有其余东西,那么这段代码就会被执行,表面上看上去好像代码就在精确指定的时间点上执行了。其余状况下,代码可能明显地等待更长时间才执行。web
(1) 某些间隔会被跳过;浏览器
(2) 多个定时器的代码执行之间的间隔可能会比预期的小。bash
为了不 setInterval()的重复定时器的这 2 个缺点,你能够用以下模式使用链式 setTimeout() 调用。函数
setTimeout(function(){ //处理中
setTimeout(arguments.callee, interval);//次优选项 可写个function代替
}, interval);
复制代码
这个模式链式调用了setTimeout(),每次函数执行的时候都会建立一个新的定时器。第二个 setTimeout()调用使用了 arguments.callee来获取对当前执行的函数的引用,并为其设置另一 个定时器。这样作的好处是,在前一个定时器代码执行完以前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。并且,它能够保证在下一次定时器代码执行以前,至少要等待指定的间隔,避 免了连续的运行。这个模式主要用于重复定时器,以下例所示。优化
setTimeout(function(){
var div = document.getElementById("myDiv");
left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if (left < 200){
setTimeout(arguments.callee, 50);
}
}, 50);
复制代码
(function(){
function draw(timestamp){
//timestamp 它是一个时间码(从1970 年1月1日起至今的毫秒数),表示下一次重绘的实际发生时间。
//计算两次重绘的时间间隔
var drawStart = (timestamp || Date.now()),
diff = drawStart - startTime; //使用 diff 肯定下一步的绘制时间
//把 startTime 重写为这一次的绘制时间 startTime = drawStart;
//重绘 UI
requestAnimationFrame(draw);
}
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame,
startTime = window.mozAnimationStartTime || Date.now();
})();
复制代码
1.setInterval 与 setTimeout 模拟的setInterval有什么区别?动画
答:ui
setInterval(() => {
//定时器代码
}, 200);
复制代码
每200ms向进程插入一次定时器代码,在第5ms,205ms,405ms处都插入了定时器代码,因为这样的一条规则:spa
当使用 setInterval()时,仅 当没有该定时器的任何其余代码实例时,才将定时器代码添加到队列中。线程
因此第5ms处,205ms都成功将定时器代码插入js进程中,可是第405ms处因为正在执行定时器代码故插入失败。使这个 间隔被跳过 ,同时执行完第一次定时器代码后紧接着执行了第二次定时器代码,使 多个定时器的代码执行之间的间隔可能会比预期的小。
setTimeout(function(){
//定时器代码
setTimeout(arguments.callee, interval);
}, interval);
复制代码
这个模式链式调用了setTimeout(),每次函数执行的时候都会建立一个新的定时器。第二个setTimeout()调用使用了arguments.callee 来获取对当前执行的函数的引用,并为其设置另一个定时器。这样作的好处是,在前一个定时器代码执行完以前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。并且,它能够保证在下一次定时器代码执行以前,至少要等待指定的间隔,避 免了连续的运行。
2.以下代码的 clearTimeout(s)是否能够清楚定时器。
var i = 0
var s = setTimeout(function(){
add()
},300)
function add(){
i++
console.log(123);
console.log(i);
if(i>20){
clearTimeout(s)
}else{
setTimeout(function(){
add()
},300)
}
}
复制代码
答:可
3.arguments.callee 是什么意思?
警告:在严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 arguments.callee(), 经过要么给函数表达式一个名字,要么使用一个函数声明.