javascript 定时器事情道理

说到 javascript 中的定时器,我们肯定会想到 setTimeout()setInterval() 这两个函数。本文将从 事宜轮回(Event Loop) 的角度来剖析二者的事变道理和区分。

setTimeout()

MDNsetTimeout 的定义为:

在指定的耽误时刻以后挪用一个函数或实行一个代码片断。

语法

setTimeout 的语法异常简朴,第一个参数为回调函数,第二个参数为延时的时刻。函数返回一个数值范例的ID唯一标示符,此ID可以用作 clearTimeout 的参数来作废定时器:

var timeoutID = window.setTimeout(code, delay);

IE0+ 还支撑回调参数的传入:

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

setInterval()

MDN 对 setInterval 的定义为:

周期性地挪用一个函数(function)或许实行一段代码。

因为 setIntervalsetTimeout 的用法一样,所以这里不再列出。

对第二个参数(delay)的申明

因为javascript 的事宜轮回机制,致使第二个参数并不代表耽误delay毫秒以后马上实行回调函数,而是尝试将回调函数到场到事宜行列。实际上,setTimeoutsetInterval 在这一点上处置惩罚又存在区分:

  • setTimeout:延时delay毫秒以后,啥也不论,直接将回调函数到场事宜行列。

  • setInterval: 延时delay毫秒以后,先看看事宜行列中是不是存在还没有实行的回调函数(setInterval的回调函数),假如存在,就不要再往事宜行列里到场回调函数了。

所以,当我们的代码中存在耗时的使命时,定时器并不会表现的如我们所想的那样。

经由过程一个例子来明白

下面的代码,原本愿望可以在 100ms200ms 的时刻(也就是恰好守候 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 了!我们来形貌一下事变的经由:

  1. 起首,第一个耗时使命(heavyTask())最先实行,它须要约莫 1000ms 才实行终了。

  2. 从耗时使命最先实行,过了 100ms, 第一个 setTimeout 的回调函数希冀实行,因而被到场到事宜行列,然则此时前面的耗时使命还没实行完,所以它只能在行列中守候,直到耗时使命实行终了它才最先实行,所以效果中我们开的看到的是: 第一个setTimeout回调实行守候时刻: 1018

  3. 第一个 setTimeout 回调一实行,又开启了第二个 setTimeout, 这个定时器也是希冀延时 100ms 以后可以实行它的回调函数。 然则,在第一个 setTimeout 又存在一个耗时使命,一切它的剧情跟第一个定时器一样,也守候了 1000ms 才最先实行。

可以用下面的图来归纳综合:
《javascript 定时器事情道理》

再来看 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定时器实行道理进行了扼要的剖析,愿望可以协助我们更深切的明白javascript。文中有形貌不当的处所可以在批评中指出。

文章地点:http://blog.mcbird.cn/2015/09/18/javascript-timers/

    原文作者:MockingBird
    原文地址: https://segmentfault.com/a/1190000003764106
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞