阻塞事件循环

5
我正在Nodeschool上学习"Functional Javascript Workshop"。其中一项练习名为"Blocking Event Loop",我很难理解它。在之前的练习中,我一直确保真正尝试理解解决方案,以便如果需要重新做问题,我将了解如何解决它(而不是第一次就开始胡乱尝试)。但这个概念真的很具有挑战性。
修改锅炉板中提供的递归重复函数,使其不会阻止事件循环(即计时器和IO处理程序可以触发)。这必然要求repeat是异步的。
一个超时被排队等待100毫秒后触发,它将打印测试结果并退出进程。repeat应释放对事件循环的控制,以允许超时中断所有操作之前。尝试在超时触发之前执行尽可能多的操作!
锅炉板
function repeat(operation, num) {
  // modify this so it can be interrupted
  if (num <= 0) return
  operation()
  return repeat(operation, --num)
}

module.exports = repeat

Solution

function repeat(operation, num) {
        if (num <= 0) return

        operation()

        // release control every 10 or so
        // iterations.
        // 10 is arbitrary.
        if (num % 10 === 0) {
          setTimeout(function() {
            repeat(operation, --num)
          })
        } else {
          repeat(operation, --num)
        }
      }

module.exports = repeat

我试图更好地理解setTimeout,并且我有点了解它的工作原理。但是,我不理解问题本身中的一些语言和概念:
修改给定的递归重复函数,使其不会阻塞事件循环(即计时器和IO处理程序可以触发)。这必然需要将repeat异步化。
我不理解解决方案如何通过使每个第10次重复异步来防止重复阻塞事件循环。我假设事件循环和事件队列是相同的东西?例如,当JavaScript代码在同步运行其代码时,单击会被放置在事件队列上,直到执行堆栈为空,然后才查看队列。如何使每个第10次重复异步可以防止队列被阻塞 - 我对“被阻塞”的理解是队列中的项目被阻塞,直到堆栈为空,此时JavaScript查看队列中的内容。所以说,在第10、20、30等处运行此代码可以使这些重复操作异步化,但在继续运行直到0之前,它是否仍会通过所有的nums呢?在此之后堆栈现在为空,JavaScript才查看队列中的内容吗?那么这个解决方案如何解决像问题所要求的那样的阻塞呢?
接下来,问题涉及“释放控制”。“控制”是什么意思,如何释放,以及它释放到哪里?
在此之后,问题说“允许超时在所有操作完成之前中断”。中断是否意味着每个第10次重复都被中断(直到同步代码结束才能继续进行)?
最后,挑战的目标是尝试在超时触发之前执行尽可能多的操作...我也不明白这与解决方案有什么关系。setTimeout似乎甚至没有设置持续时间。

你似乎卡在了“每10次重复”上,这让我觉得你可能从错误的角度来考虑这个问题。 - Jaromanda X
@JaromandaX 我引用每10次重复的意图主要是为了标记setTimeout函数将在解决方案中运行的位置。从阅读其他帖子关于这个特定问题的一些帖子来看,选择10并不重要 - 它可以是任何数字。无论如何,我希望被引导到正确的角度。我的目标是真正理解这个问题背后的概念,以便我能够自己回答它。 - user4853
我的观点是,我认为你的解决方案方式不正确。我可能(很可能)是错的。我的观察或许可以促使你退后一步重新评估问题,然而,再次审视问题,我发现这个“解决方案”不是的解决方案,而你也很难理解它。 - Jaromanda X
1个回答

10

消息队列(也称为事件队列)是要处理的 消息 列表

事件循环按顺序逐个处理这些消息直至完成

您发布的重复函数的阻塞版本如下:

function repeat(operation, num) {
    if (num <= 0) return

    operation()

    repeat(operation, --num)
}

您可以看到,这个函数会递归调用自身,直到num <= 0,因此在num <= 0之前,它不会完成,因此事件循环中将不会处理任何新消息,直到它完成。

与非阻塞版本相比,这就是其中的区别。

function repeat(operation, num) {
    if (num <= 0) return

    operation()

    // release control every 10 or so
    // iterations.
    // 10 is arbitrary.
    if (num % 10 === 0) {
        setTimeout(function() {
            repeat(operation, --num)
        })
    } else {
        repeat(operation, --num)
    }
}

每10个迭代,这个函数不会递归调用repeat函数,而是通过setTimeout将下一次调用repeat放在消息队列的末尾,然后不执行任何其他操作(因此已经完成)

这使得事件循环能够处理在调用setTimeout之前的10次repeat期间放置在消息队列中的所有消息,并且只有在这些消息已经被完全处理后,setTimeout中的回调才会被执行,这将是repeat函数的下一个迭代。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接