【问题标题】:setTimeout waits too long in Node.jssetTimeout 在 Node.js 中等待时间过长
【发布时间】:2017-07-08 05:03:49
【问题描述】:

我认为我对 setTimeout 在 JavaScript 中的作用存在误解。我有这个脚本:

function recursiveFibonacci(n) {
    if ( n === 0 ) {
        return 1;
    } else if ( n === 1 ) {
        return 1;
    } else {
        return recursiveFibonacci(n-1) + recursiveFibonacci(n-2);
    }
}

setTimeout( () => {console.log("one second");}, 1000);

console.log(recursiveFibonacci(42));

我预计会发生的是,recursiveFibonacci 在斐波那契数列的第 43 个值处开始运行。这在我的电脑上大约需要 4 秒。因此,在工作 1 秒后,评估将被中断,控制台将记录:

one second

然后大约 3 秒后登录:

433494437

相反,4 秒后,控制台会记录:

433494437
one second

一下子。为什么会这样?我怎样才能让setTimeout 工作? JavaScript 解释器是否实际上并没有被setTimeout 中断,而是如果它完成了其他工作,那么它会等到给定的时间过去后才调用给定的函数?

编辑:

我发现这个工具对于理解相关概念非常有用:

Loupe

【问题讨论】:

  • setTimeout 是一个函数,它将在给定的时间内与其他运行代码并行(几乎)执行命令。您似乎对延迟更感兴趣,例如 $.delay() 函数。是的,口译员没有被打断。
  • @Mikael, $ 未在节点中定义。我要导入什么来使用它?
  • jQuery. $ 在 JavaScript 上下文中通常表示 jQuery,抱歉没有说得更清楚。祝你好运。
  • 好的,我输入了sudo npm install jquery,然后在 REPL 中输入了var $ = require('jquery');,然后我输入了$.delay(()=>{console.log("hi");},1000);,导致错误TypeError: $.delay is not a function。事实上,REPL 中的> $ 产生[Function]
  • @SeanLetendre - Mikael 派你去追逐野鹅。 $.delay() 在这里根本帮不了你,你不应该尝试在服务器上使用 jQuery 来设置计时器。这是 nodejs 如何工作的核心问题。详细解释请看我下面的回答。

标签: javascript node.js asynchronous settimeout


【解决方案1】:

node.js 中的 Javascript 是事件驱动和单线程的。由于您的 reverseFibonacci() 函数是同步的,因此在完成之前不允许运行其他任何东西。

因此,即使计时器触发并将计时器回调放入内部 nodejs 事件队列,JS 解释器也无法获取事件队列中的下一个事件,直到您的 reverseFibonacci() 函数完成。

setTimeout() 不会中断当前运行的 Javascript。相反,它将一个事件放入事件队列中,当当前运行的 Javascript 完成后,JS 解释器会将下一个事件从事件队列中拉出并运行。

所以,你的场景中的事件顺序是这样的:

  1. 从现在开始安排一个 1 秒的计时器事件。
  2. 开始运行reverseFibonacci()
  3. 定时器在 1 秒后触发(在 nodejs 内部)并将一个事件插入到 nodejs 事件队列中。
  4. 4 多秒后,您的 reverseFibonacci(42) 完成运行。
  5. 由于当前运行的 JS 已经完成,JS 解释器从事件队列中拉出下一个事件并处理它,从而最终调用您的计时器回调。

带有setTimeout() 的计时器会在其预定时间之后尽快运行,但如果 Javascript 解释器正忙于运行其他代码,它们不会中断该代码。计时器系统将一个事件放入事件队列中,当其他代码完成后,Javascript 解释器将从事件队列中拉出下一个事件并运行它(您的计时器回调将被调用)。

【讨论】:

    【解决方案2】:

    这是因为您的函数执行同步工作。

    node event loop 的本质是异步的。只要您的函数完成执行,或者您将执行异步操作 - 将调用 nextTick。第一项很可能是你的 setTimout 函数。

    这是一个带有预期异步块的示例,它可以按您的预期工作:

    function sleep(ms) {
      return new Promise( (resolve, reject) => {
        setTimeout(resolve, ms);
      });
    }
    
    async function recursiveFibonacci(n) {
      await sleep(10);
      if ( n === 0 ) {
          return 1;
      } else if ( n === 1 ) {
          return 1;
      } else {
          return await recursiveFibonacci(n-1) + await recursiveFibonacci(n-2);
      }
    }
    
    setTimeout( () => {console.log("one second");}, 1000);
    
    recursiveFibonacci(25).then(console.log);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-27
      • 1970-01-01
      • 2022-11-03
      • 2020-09-14
      • 2020-04-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多