setInterval 和 setTimeout 如何工作?

IT技术 javascript browser v8 spidermonkey chakra
2021-02-09 00:05:54

我处于尴尬的境地

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

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

我想在特定时间只运行代码的一部分,然后切换到另一部分。如果是这样,那么很多setIntervalorsetTimeout 调用会影响性能吗?

5个回答

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

当调用setTimeoutor 时setInterval,浏览器中的计时器线程开始倒计时,当时间到时将回调函数放入 javascript 线程的执行堆栈中。回调函数不会在堆栈中位于其上方的其他函数完成之前执行。所以如果time up时还有其他耗时的函数在执行,setTimeout则不会及时完成回调

“在堆栈中位于其上方的其他函数完成之前,不会执行回调函数。”。我想这不是真的。请检查小提琴jsfiddle.net/er_markar/ycjc77xa
2021-03-16 00:05:54
让我感到困惑的是,计时器线程将操作放在执行队列上是否不安全,因为它与管理队列的线程不在同一线程上。除非计时器线程可以以某种方式暂停事件线程,否则这不会导致队列处于不一致状态吗?
2021-03-21 00:05:54
尽管浏览器有多个线程,但 setInterval 的调度与脚本相同
2021-03-24 00:05:54
计时线程究竟是做什么的?它是一个检查系统时间的循环吗?
2021-03-29 00:05:54
@SunilGarg 你的小提琴不正确。它应该是 setTimeout(alert, 3000) 以按照您的假设工作。执行 setTimeout(alert(), 3000) 导致首先评估 alert(),然后将 alert() 的结果传递给 setTimeout
2021-04-13 00:05:54

JavaScript 中的 setTimeout / setInterval 如何工作

浏览器具有定时器功能的 API,就像事件 ex 的 API。

'点击'

'滚动'

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

function listener(){
    ...
}

setTimeout(listener, 300)

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

foo()

看看函数执行是如何在 javascript 中工作的

此时,根据我们在上面编写的代码,我们的调用堆栈将如下所示

调用堆栈 -> foo

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

会发生什么?

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

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

这里 javascript 魔法出现了

当 300 毫秒到期时,浏览器“Timer API”启动并将超时处理程序放入“消息队列”

此时上图中的“消息队列”看起来像

消息队列 -> setTimeout:listner

调用堆栈 -> foo

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

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

此时上图中的消息队列看起来像

消息队列 ->

调用堆栈 -> 监听器

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

目前尚不清楚网页的 settimeout 调用正在进行时有什么开销,但没有其他任何事情发生。事件循环是运行还是等待事件?计时线程是否循环运行?是否有任何 CPU 负载?
2021-03-23 00:05:54

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

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

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

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

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

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

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

窗口{postMessage:ƒ,模糊:ƒ,焦点:ƒ,关闭:ƒ,框架:窗口,...}

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

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);

设置间隔()

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

  • setTimeout 代码在上
    一次回调执行后将始终有至少 10 毫秒的延迟(最终可能会更多,但绝不会更少),而 setInterval 将尝试每 10 毫秒执行一次回调,而不管最后一次回调是何时执行的。

  • 如果一个计时器被阻止立即执行,它将被延迟
    到下一个可能的执行点(这将比所需的延迟更长)。如果间隔的执行时间足够长(长于指定的
    延迟),则它们可以无延迟地背靠背执行

只是关于我的经验的说明,如果您打算使用 setTimeout(),该函数将始终被延迟(我的意思是稍后执行)或在未“超时”的进一步代码之后执行。即使对于延迟 = 0 且由事件循环引起的函数,也可能发生这种情况。

请参阅下面的示例:

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

console.log('First');