【问题标题】:How does setInterval and setTimeout work?setInterval 和 setTimeout 是如何工作的?
【发布时间】:2014-03-29 20:39:34
【问题描述】:

我的处境很尴尬

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

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

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

【问题讨论】:

  • 当然。如果你这样做while(true);,那么超时和间隔将永远不会运行。
  • 您放置的每一段代码都会影响性能,更重要的是间隔和超时,因为您正在使浏览器不断处理以减少变量。所以如果你有很多,你的页面会运行得很慢
  • 他们只推迟执行,直到所有其他代码都执行完毕并且线程空闲,他们不创建新线程。
  • 想象一个按执行时间排序的任务队列。如果执行时间

标签: javascript browser v8 spidermonkey chakra


【解决方案1】:

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)

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

【讨论】:

    【解决方案2】:

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

    请看下面的例子:

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

    【讨论】:

      【解决方案3】:

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

      setInterval()

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

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

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

      【讨论】:

        【解决方案4】:

        setTimeout / setInterval 在 JavaScript 中的工作原理

        浏览器具有用于 Timer 功能的 API,就像用于事件 ex 的 API。

        '点击'

        '滚动'

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

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

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

        调用堆栈 -> foo

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

        然后会发生什么?

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

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

        这里 javascript 魔法出现了

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

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

        消息队列 -> setTimout:listner

        还有

        调用堆栈 -> foo

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

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

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

        消息队列->

        还有

        调用堆栈 -> 监听器

        这就是 setTimeout 和 setInterval 的工作方式,即使我们在 setTimeout 中指定 300 毫秒,它也会在“foo”完成它的执行后执行,在这种情况下,即 1 秒后。 这就是为什么在 setTimeout/setInterval 中指定的计时器表示 "Minimum Time" 延迟函数执行的原因。

        【讨论】:

        • 目前尚不清楚在对网页进行 settimeout 调用时有什么开销,但没有其他任何事情发生。事件循环是运行还是等待事件?计时线程是否循环运行?是否有任何 cpu 负载?
        【解决方案5】:

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

        当调用setTimeoutsetInterval 时,浏览器中的定时器线程开始倒计时,当时间到时将回调函数放入javascript 线程的执行堆栈中。在堆栈中它上面的其他函数完成之前,不会执行回调函数。所以如果超时还有其他耗时的函数在执行,setTimeout的回调将无法及时完成。

        【讨论】:

        • 虽然浏览器有多个线程,但是setInterval的调度与脚本相同
        • “回调函数在堆栈中它上面的其他函数完成之前不会执行。”。我想这不是真的。请检查小提琴jsfiddle.net/er_markar/ycjc77xa
        • 让我困惑的是,定时器线程将操作放在执行队列上是不是不安全,因为它与管理队列的线程不在同一个线程上。除非计时器线程可以以某种方式暂停事件线程,否则这不会导致队列处于不一致状态吗?
        • @SunilGarg 你的小提琴不正确。应该 setTimeout(alert, 3000) 按照您的假设工作。执行 setTimeout(alert(), 3000) 会首先评估 alert(),然后将 alert() 的结果传递给 setTimeout
        • 计时线程到底是做什么的?是检查系统时间的循环吗?
        猜你喜欢
        • 1970-01-01
        • 2020-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多