为什么我看不到任务队列堆积起来?

问题描述 投票:0回答:1

我正在阅读并与 chatGPT 讨论

setTimeout
setInterval
到底做什么。据我了解,主 JavaScript 执行线程向 Web Timer API 发送回调函数和间隔或设置时间(以毫秒为单位),之后回调函数应添加到任务队列中。将这两件事发送到 Web API 后,执行线程返回到主调用堆栈并继续执行其余的同步代码。现在,当 Web API 收到回调和时间(例如其
1000ms
)时,它会启动一个计时器,并在每个
1000ms
时将回调发送到任务队列。在
setTimeout
的情况下,当 Web API 收到回调和时间(例如
5000ms
)时,它会启动一个计时器。此计时器到达
5000ms
后,回调函数将添加到任务队列中。现在,这些任务队列函数何时添加到调用堆栈并执行取决于其余 JavaScript 代码需要多长时间才能执行,因此在调用堆栈清除之前,函数可能会在任务队列中累积。这就是 chatGPT 所说的“聚集”。并声称,如果在清除调用堆栈和开始执行任务队列中的函数之间经过了足够的时间,则可以快速连续执行不遵守 setInterval 中设置的间隔的回调。

所以我写了一个程序来实验:

function limitedRepeater() {
  return setInterval(() => console.log("hi for now"), 1000);
}

const someId = limitedRepeater();
setTimeout(() => clearInterval(someId), 5000);

这很容易理解。 setTimeout 计时器初始化后,

console.log("hi for now")
调用会每 1000 毫秒添加到任务队列中,持续 5000 毫秒。这是由 Windows PowerShell 命令 Measure-Command 计算得出的输出和经过的时间:

Output:

hi for now

hi for now

hi for now

hi for now

Measure-Command {node .\asynchronous_js_frontendmasters.js}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 5
Milliseconds      : 28
Ticks             : 50285701
TotalDays         : 5.82010428240741E-05
TotalHours        : 0.00139682502777778
TotalMinutes      : 0.0838095016666667
TotalSeconds      : 5.0285701
TotalMilliseconds : 5028.5701

现在,如果我在 setInterval 函数调用之后但在 setTimeout 调用之前包含一段线程阻塞代码,会发生什么情况:

function limitedRepeater() {
  return setInterval(() => console.log("hi for now"), 1000);
}

const someId = limitedRepeater();
let sum = 0;
for (let i = 0; i < 90000000000; i++) {
  sum += i;
}
setTimeout(() => clearInterval(someId), 5000);

这个 for 循环应该为主执行线程创建一个重要的块。确实如此:

Output:

hi for now

hi for now

hi for now

hi for now

hi for now

Measure-Command {node .\asynchronous_js_frontendmasters.js}

Days              : 0
Hours             : 0
Minutes           : 1
Seconds           : 7
Milliseconds      : 231
Ticks             : 672318270
TotalDays         : 0.000778146145833333
TotalHours        : 0.0186755075
TotalMinutes      : 1.12053045
TotalSeconds      : 67.231827
TotalMilliseconds : 67231.827

它增加了超过一分钟的执行时间!但只多执行一次

console.log("hi for now")
。 chatGPT 声称传递给 Web API 的间隔或计时器控制将回调添加到任务队列之间所经过的时间,而不一定是调用堆栈执行之间所经过的时间。但如果是这种情况,在 for 循环并调用
console.log("hi for now")
之前,是否应该将更多的
setTimeout
执行添加到任务队列中?我缺少什么?谢谢!

javascript settimeout setinterval callstack event-loop
1个回答
0
投票

设置间隔

帖子中的引用对

setInterval
的实现存在严重误解:

现在,当 Web API 收到回调和时间(假设为 1000 毫秒)时,它会启动一个计时器,并每隔 1000 毫秒将回调发送到任务队列。

HTML 规范的

8.6 计时器 提供了“任务初始化步骤”列表,总共包含 13 个步骤,浏览器必须遵循这些步骤来实现计时器 API 调用。绿色注释是解释性的,只需要看两个步骤:

  • 计时器初始化列出的步骤中的第 8 步创建一个task,以在发生超时时放入事件循环的作业队列中。该任务在执行时调用提供给计时器调用的处理程序。

  • 步骤8.4是任务运行时执行的。如果 repeat 非零,则要求浏览器再次运行完整的初始化步骤集,这意味着它是

    setInterval
    调用的一部分。

    在每次间隔超时后,都会使用提供给

    setTimeout
    调用者的
    id
    来模拟新的
    setInterval
    调用。


实验

    调用
  1. setInterval
    ,超时时间为 1000 毫秒。这最初充当
    setTimeout
    并设置一个系统计时器来将任务排队以调用计时器回调。

  2. 阻塞代码会阻塞 JavaScript 线程超过 1000 毫秒。这会阻止执行 (1) 中创建的任务。没有构建

    setInterval
    回调,因为只安排了一个任务,并且在主线程被阻塞时无法执行该任务。

  3. 阻塞代码设置一个 5 秒的计时器,以清除 (1) 中创建的间隔计时器并返回到事件循环。

  4. 在(1)中创建的任务可以并且现在已经执行。作为其执行的一部分,它创建控制台输出并且创建一个具有相同计时器ID的新的1000ms计时器。

  5. 在接下来的 5 秒内,间隔计时器实现又创建并运行 4 个任务,总共在控制台上创建 5 个条目。

  6. (3)中创建的定时器任务运行并清除间隔定时器。 但随后返回到事件循环。

实验成功了!

© www.soinside.com 2019 - 2024. All rights reserved.