在node.js中一直运行一个函数

时间:2016-07-12 08:43:57

标签: node.js promise ecmascript-6 q

为了让一个函数的单个实例一直在运行,以下方法在某些时候会出现问题吗?

const Q = require('q');

(function main() {
        Q.fcall(somePromiseFunction).then(_ => {
                main();
        });
})();

我想知道一段时间后递归内存堆栈是否会溢出而程序是否会终止?

1 个答案:

答案 0 :(得分:2)

令人惊讶的是,不!我相信你的代码运行良好。

JavaScript中的异步操作(例如setTimeout()XMLHttpRequestPromise使用称为Event Loop的内容在程序的正常流程之外执行。实际上,当异步事件发生时,它会被添加到队列中。 (完全独立于堆栈)。一旦堆栈清空,事件循环将开始处理这些排队的消息,逐个执行它们的相关功能。

几乎所有JavaScript中的异步都以这种方式工作,其中包括Q promises库。因此,就您的示例而言,这是(非常)简化的解释:

    调用
  • main(),创建一个新的堆栈帧。
  • 调用
  • Q.fcall(somePromiseFunction),创建第二个堆栈帧(可能是第三个,当调用somePromiseFunction时。这会在后台启动异步操作,并将函数传递给{{1}设置为回调。
  • then现已返回,因此清除了这些堆栈帧。
  • Q.fcall(somePromiseFunction)也已到达终点,因此也会被清除 - 我们会重新回到空堆栈。
  • 异步操作完成后,会将一条消息推送到事件队列,并将main()回调与之关联。
  • 堆栈为空,因此事件循环开始处理。
  • 您的回调会被执行。

这里要注意的重要一点是您的代码不会递归!您的then回调仅从事件队列中调用,而不是由then调用,因此堆栈能够在迭代之间无问题地清除。

我建议观看演讲'What the heck is the event loop anyway?',了解有关这一切是如何运作的更多信息 - 他们解释的原因是它最终为我点击了。

修改 在评论中回答您的问题时,我会澄清一些事情。递归函数,以其最简单的形式,如下所示:

main()

调用函数会分配一个堆栈帧,当它返回时会被清除。但是,如果没有某种突破循环的方式,像这样的递归函数永远不会返回 - 它将继续运行,无休止地分配堆栈帧,直到出现堆栈溢出错误。这并不意味着你不应该在JavaScript中使用递归 - 它有时候非常有用!但是你需要注意的是,如果它在没有返回的情况下进行过多的递归,那么你就会成为最重要的。

在回答你的另一个问题时 - 事件循环/你的回调并不等待function recursive() { recursive(); } 特别结束,它只是等待堆栈清除。这里简化了堆栈在程序中的作用:

main()

将此与我无休止的重复功能相比较:

[]
[main]
[main, fcall]
[main, fcall, somePromiseFunction]
[main, fcall]
[main]
[main, then]
[main]
[]                                 // Main returned, so the stack is clear
[yourCallback]                     // The event loop kicks in, runs your callback
[yourCallback, main]
...                                // Above steps repeat
[yourCallback]                     // Main returned
[]                                 // Stack clears, so the event loop kicks in again