Javascript setInterval和setTimeout是如何工作的?

Javascript setInterval和setTimeout是如何工作的?,javascript,browser,v8,spidermonkey,chakra,Javascript,Browser,V8,Spidermonkey,Chakra,我当时处境尴尬 我在纯JavaScript上工作了将近3年,我知道JavaScript是单线程语言, 您可以使用setInterval和setTimeout函数模拟异步执行 但当我想到它们如何工作时,我无法清楚地理解它。 那么这些函数是如何影响执行上下文的呢 我假设在特定的时间内只运行代码的一部分,然后切换到 另一部分。如果是这样,那么会有很多setInterval或setTimeout 调用会影响性能?Javascript是单线程的,但浏览器不是。浏览器至少有三个线程:Javascript引擎

我当时处境尴尬

我在纯JavaScript上工作了将近3年,我知道JavaScript是单线程语言, 您可以使用
setInterval
setTimeout
函数模拟异步执行

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

我假设在特定的时间内只运行代码的一部分,然后切换到 另一部分。如果是这样,那么会有很多
setInterval
setTimeout

调用会影响性能?

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


调用
setTimeout
setInterval
时,浏览器中的计时器线程开始倒计时,当time up时,将回调函数放入javascript线程的执行堆栈中。在堆栈中位于回调函数之上的其他函数完成之前,不会执行回调函数。因此,如果在超时时执行其他耗时的函数,
setTimeout
的回调将无法及时完成

setTimeout/setInterval在JavaScript中的工作原理 浏览器具有用于计时器功能的API,就像用于事件ex的API一样

“点击”

“滚动”

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

function listener(){
    ...
}

setTimeout(listener, 300)

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

foo()
函数侦听器(){
...
}
设置超时(侦听器,300)
函数foo(){
对于(变量i=0;i<10000;i++){
控制台日志(i)
}
}
foo()
此时,根据我们在上面编写的代码,我们的调用堆栈将如下所示

调用堆栈->foo

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

那么会发生什么

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

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

在这里,javascript魔术出现在画面中

当300ms过期时,浏览器“计时器API”启动并将超时处理程序放入“消息队列”

此时,上图中的“消息队列”如下所示

消息队列->设置超时:listner

调用堆栈->foo

当“调用堆栈”变为空时,即foo完成其执行,如图所示的“事件循环”将从消息队列中获取消息并将其推入堆栈

“事件循环”的唯一任务是当“调用堆栈”变为空且“消息队列”中有条目时,将消息从“消息队列”中出列,并将其推入“调用堆栈”

此时,上图中的消息队列如下所示

消息队列->

调用堆栈->侦听器

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

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代码在执行后将始终至少有10毫秒的延迟
    上一次回调执行(可能会更多,但永远不会 小于)而setInterval将尝试执行回调 每10毫秒一次,无论最后一次回调何时执行

  • 如果计时器被阻止立即执行,它将被延迟
    直到下一个可能的执行点(将比 所需的延迟)。间隔可以连续执行,没有延迟 如果它们需要足够长的时间执行(比指定的时间长
    延迟)


根据我的经验,如果要使用setTimeout(),函数将始终延迟(我的意思是稍后执行)或在未“超时”的进一步代码之后执行。即使延迟为0且由事件循环引起的函数也可能发生这种情况

见下例:

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

console.log('First');

Javascript是单线程的,但浏览器不是

有1个堆栈,在其中执行函数和语句。 有1个队列,其中函数排队执行。 有一些web API可以在事件表的setTimeout和setInterval中定义的特定时间内保存函数

当javascript引擎逐行执行js文件时,如果它发现一行as语句或函数调用,则将其加载到堆栈上并执行,但如果是setTimeout或setInterval调用, 然后,TIME API(浏览器的web API之一)取出与setTimeout或setInterval关联的函数处理程序并保持