setTimeout在Node.js中等待太长时间

时间:2017-07-08 05:03:50

标签: javascript node.js asynchronous settimeout

我认为我误解了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

2 个答案:

答案 0 :(得分:6)

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

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

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

因此,您的方案中的事件顺序如下:

  1. 从现在起安排计时器事件1秒钟。
  2. 开始运行reverseFibonacci()
  3. 计时器在1秒后触发(在nodejs内部)并将事件插入nodejs事件队列。
  4. 4秒以后,reverseFibonacci(42)完成了运行。
  5. 由于当前正在运行的JS已经完成,JS解释器会将下一个事件从事件队列中拉出并对其进行处理,从而最终调用您的计时器回调。
  6. 计时器安排setTimeout()在预定时间后尽快运行,但如果Javascript解释器正在忙于运行其他代码,则它们不会中断该代码。计时器系统将一个事件放入事件队列,当其他代码完成时,Javascript解释器将把下一个事件从事件队列中拉出并运行它(你的计时器回调将被调用)。

答案 1 :(得分:1)

这是因为你的功能可以同步工作。

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