说到 javascript 中的定时器,我们肯定会想到 setTimeout()
和 setInterval()
这两个函数。本文将从 事宜轮回(Event Loop) 的角度来剖析二者的事变道理和区分。
setTimeout()
MDN对 setTimeout
的定义为:
在指定的耽误时刻以后挪用一个函数或实行一个代码片断。
语法
setTimeout
的语法异常简朴,第一个参数为回调函数,第二个参数为延时的时刻。函数返回一个数值范例的ID唯一标示符,此ID可以用作 clearTimeout
的参数来作废定时器:
var timeoutID = window.setTimeout(code, delay);
IE0+ 还支撑回调参数的传入:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
setInterval()
MDN 对 setInterval 的定义为:
周期性地挪用一个函数(function)或许实行一段代码。
因为 setInterval
和 setTimeout
的用法一样,所以这里不再列出。
对第二个参数(delay)的申明
因为javascript 的事宜轮回机制,致使第二个参数并不代表耽误delay毫秒以后马上实行回调函数,而是尝试将回调函数到场到事宜行列。实际上,setTimeout
和 setInterval
在这一点上处置惩罚又存在区分:
setTimeout:延时delay毫秒以后,啥也不论,直接将回调函数到场事宜行列。
setInterval: 延时delay毫秒以后,先看看事宜行列中是不是存在还没有实行的回调函数(setInterval的回调函数),假如存在,就不要再往事宜行列里到场回调函数了。
所以,当我们的代码中存在耗时的使命时,定时器并不会表现的如我们所想的那样。
经由过程一个例子来明白
下面的代码,原本愿望可以在 100ms
和 200ms
的时刻(也就是恰好守候 100ms
)挪用回调函数:
var timerStart1 = now();
setTimeout(function () {
console.log('第一个setTimeout回调实行守候时刻:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
console.log('第二个setTimeout回调实行守候时刻:', now() - timerStart2);
}, 100);
}, 100);
// 输出:
// 第一个setTimeout回调实行守候时刻: 106
// 第二个setTimeout回调实行守候时刻: 107
如许的效果看上去恰是我们所想的那样,然则一旦我们在代码中到场了耗时的使命时刻,效果就不像我们所希冀的那样了:
var timerStart1 = now();
setTimeout(function () {
console.log('第一个setTimeout回调实行守候时刻:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
console.log('第二个setTimeout回调实行守候时刻:', now() - timerStart2);
}, 100);
heavyTask(); // 耗时使命
}, 100);
var loopStart = now();
heavyTask(); // 耗时使命
console.log('heavyTask消耗时刻:', now() - loopStart);
function heavyTask() {
var s = now();
while(now() - s < 1000) {
}
}
function now () {
return new Date();
}
// 输出:
// heavyTask消耗时刻: 1015
// 第一个setTimeout回调实行守候时刻: 1018
// 第二个setTimeout回调实行守候时刻: 1000
两个 setTimeout
的守候事宜因为耗时使命的存在不再是 100ms
了!我们来形貌一下事变的经由:
起首,第一个耗时使命(
heavyTask()
)最先实行,它须要约莫1000ms
才实行终了。从耗时使命最先实行,过了
100ms
, 第一个setTimeout
的回调函数希冀实行,因而被到场到事宜行列,然则此时前面的耗时使命还没实行完,所以它只能在行列中守候,直到耗时使命实行终了它才最先实行,所以效果中我们开的看到的是:第一个setTimeout回调实行守候时刻: 1018
。第一个
setTimeout
回调一实行,又开启了第二个setTimeout
, 这个定时器也是希冀延时100ms
以后可以实行它的回调函数。 然则,在第一个setTimeout
又存在一个耗时使命,一切它的剧情跟第一个定时器一样,也守候了 1000ms 才最先实行。
可以用下面的图来归纳综合:
再来看 setInterval
的一个例子:
var intervalStart = now();
setInterval(function () {
console.log('interval距定义定时器的时刻:', now() - loopStart);
}, 100);
var loopStart = now();
heavyTask();
console.log('heavyTask消耗时刻:', now() - loopStart);
function heavyTask() {
var s = now();
while(now() - s < 1000) {
}
}
function now () {
return new Date();
}
// 输出:
// heavyTask消耗时刻: 1013
// interval距定义定时器的时刻: 1016
// interval距定义定时器的时刻: 1123
// interval距定义定时器的时刻: 1224
上面这段代码,我们希冀每隔 100ms
就打出一条日记。相对于 setTimeout
的区分, setInterval
在预备把回调函数到场到事宜行列的时刻,会推断行列中是不是另有未实行的回调,假如有的话,它就不会再往行列中添加回调函数。 不然,会涌现多个回调同时实行的状况。
可以用下面的图来归纳综合:
总结
上面临javascript定时器实行道理进行了扼要的剖析,愿望可以协助我们更深切的明白javascript。文中有形貌不当的处所可以在批评中指出。