计时器
var id = setTimeout(fn, delay)
:开始一个timer,将在delay时间后调用fnvar id = setInterval(fn, delay)
clearInterval(id)
,clearTimeout(id)
计时器执行时间
计时器的延迟是没有保证的。
由于浏览器中的 JS 都在单线程里执行的,因此只有在执行过程中空闲时才会执行异步事件(包括鼠标点击和计时器)。
最直观的例子:
setTimeout(function() { console.log(1);}, 0);console.log(2);复制代码
输出结果是 2 1。上例中看似在 0ms 后打印 1,但事实上是在执行完同步的 JS 后才执行计时器内的代码。
异步队列
异步事件(包括计时器、鼠标事件、XMLHttpRequest完成等)会被加入一个队列等待执行,队列的实现因浏览器而异。
在同步代码执行时,把所有异步事件加入队列中。当同步代码执行完后(18ms),先执行鼠标点击事件(10ms),然后执行计时器,这时已经过了 28ms。
Intervals 不关心当前执行的是什么,他们会无差别地添加到队列中。图中的 interval 注册是 10ms 间隔,但是在执行过程中因为 interval 回调函数本身的执行时间大于或等于 10ms,就会牺牲掉 interval 每个回调函数之间的间隔。
setTimeout 和 setInterval
setTimeout(function(){ // ... setTimeout(arguments.callee, 10);}, 10); setInterval(function(){ // ...}, 10);复制代码
上面的两种写法看起来结果是一样的,但是实际上有差别。由于 setTimeout 的写法是每次在回调函数执行时添加一个新的计时器,所以这个回调每次执行的间隔至少是 10ms。而 setInterval 的写法使回调每 10ms 执行一次。
要点总结
- JavaScript 单线程使得异步事件要排队等待执行。
setTimeout
和setInterval
本质上不同。setInterval
注册的间隔是回调开始执行的间隔,如果回调执行超过间隔时间,就会连续无延迟地执行。
参考
https://johnresig.com/blog/how-javascript-timers-work/