setInterval和setTimeout如何工作?

时间:2014-02-26 19:24:40

标签: javascript browser v8 spidermonkey chakra

我处境尴尬

我正在使用纯JavaScript近3年,我知道JavaScript是单线程语言, 并且您可以使用setIntervalsetTimeout函数模拟异步执行,

但当我想到他们如何工作时,我无法清楚地理解它。 那么这些函数如何影响执行上下文呢?

我认为在特定时间内只运行代码的一部分并在切换到之后 另一部分。如果是这样,则会有很多setIntervalsetTimeout 电话会影响表现吗?

5 个答案:

答案 0 :(得分:18)

Javascript是单线程的,但浏览器不是。浏览器至少有三个线程:Javascript引擎线程,UI线程和时序线程,其中setTimeoutsetInterval的时间由时序线程完成。

当调用setTimeoutsetInterval时,浏览器中的计时器线程开始倒计时,并且当超时时将回调函数放入javascript线程的执行堆栈中。在堆栈中的其他函数完成之前,不执行回调函数。因此,如果在时间到时执行其他耗时的函数,setTimeout的回调将无法及时完成。

答案 1 :(得分:6)

setTimeout / setInterval如何在JavaScript中运行

浏览器具有用于计时器功能的API,就像事件ex的API一样。

  

'点击'

     

'滚动'

假设您的应用程序中有以下代码

function listener(){
    ...
}

setTimeout(listener, 300)

function foo(){
    for(var i = 0; i < 10000; i++){
        console.log(i)
    }
}

foo()

See How Function Execution work's in javascript

此时根据我们上面编写的代码,我们的调用堆栈看起来像

  

Call Stack - &gt; FOO

让我们假设foo将需要1来完成它的执行,因为我们已经在代码中定义了1个超时,并且我们在“foo”完成之前运行它,它的执行是在300ms

那会发生什么?

javascript是否会停止执行foo并开始执行setTimeout?

正如我们已经知道javascript是单线程的所以它必须在继续前进之前完成foo的执行,但是浏览器如何确保在执行foo之后“setTimeout”会执行?

这里javascript魔法进入画面

当300ms过期时,浏览器“Timer API”启动并将超时处理程序置于“Message Queue”

此时上图中的“Message Queue”看起来像

  

消息队列 - &gt; setTimout:听者

并且

  

Call Stack - &gt; FOO

当“Call Stack”变为空时,foo完成它的执行时,如图所示的“Event Loop”将从消息队列中取出消息并将其推入堆栈

“事件循环”的唯一工作是“调用堆栈”变为空并且“消息队列”中有条目然后将消息从“消息队列”出列并将其推入“调用堆栈”

此时上面图片中的Message Queue看起来像

  

消息队列 - &gt;

并且

  

Call Stack - &gt;听者

这就是setTimeout和setInterval如何工作,即使我们在setTimeout中指定300 ms它将在“foo”完成它在这种情况下执行后执行,即在1s之后执行。 这就是为什么setTimeout / setInterval中指定的计时器指示“最短时间”延迟执行功能的原因。

答案 2 :(得分:2)

JavaScript是单线程的,但浏览器不是。

有1个堆栈,其中执行了函数和语句。 有1个队列,其中要执行功能的队列。 有一些Web API可以在特定时间保存该功能,这些事件在事件表的setTimeout和setInterval中定义。

当javascript引擎逐行执行js文件时,如果它找到一行作为语句或函数调用,则会将其加载到堆栈上并执行,但如果是setTimeout或setInterval调用, 然后与setTimeout或setInterval关联的函数处理程序由TIME API(浏览器的Web API之一)取出并保留该时间。

这个时间结束后,Time Api将该函数放在执行队列的末尾。

现在该函数的执行取决于队列中提前的其他函数调用。

注意:此函数调用是在窗口对象上调用的。

setTimeout(function () {console.log(this)}, 300)

  

Window {postMessage:ƒ,blur:ƒ,focus:ƒ,close:ƒ,frames:Window,...}

答案 3 :(得分:1)

JavaScript是一种单线程脚本语言,因此它可以一次执行一段代码(由于其单线程性质),这些代码块中的每一块都“阻塞”其他异步事件的进度。这意味着当发生异步事件时(如鼠标单击,计时器触发或XMLHttpRequest完成),它将排队等待稍后执行。

<强>的setTimeout()         当你使用setTimeout()时,它只会在轮到队列时才会执行,如果由于某种原因,setTimeout之前的事件(setTimeout)会阻塞,setTimeout可能比setTimeout()函数中的指定时间延迟。在执行setTimeout回调函数期间,如果发生任何事件(例如点击事件),它将排队等待稍后执行。

setTimeout(function(){
  /* Some long block of code... */
  setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
  /* Some long block of code... */
}, 10);

<强>的setInterval()

  • 与setTimeout类似,但不断调用该函数(使用 (每次延迟)直到取消。

  • setTimeout代码将始终至少有10ms的延迟 以前的回调执行(它可能最终会更多,但永远不会 less)而setInterval将尝试执行回调 无论上次回调何时执行,每10毫秒一次。

  • 如果计时器被阻止立即执行,则会延迟 直到下一个可能的执行点(将比...更长) 期望的延迟)。间隔可以无延迟地背靠背执行 如果他们花了足够长的时间来执行(超过指定的时间) 延迟)。

答案 4 :(得分:0)

关于我的经验,请注意,如果要使用setTimeout(),则该函数将始终被延迟(我的意思是稍后执行),或者在没有“超时”的其他代码之后执行。即使事件延迟引起的延迟= 0的函数也可能发生这种情况。

请参见以下示例:

setTimeout(function() {
    console.log('Second');
}, 0)

console.log('First');