【问题标题】:running a function all the time in node.js在 node.js 中一直运行一个函数
【发布时间】:2016-11-14 09:41:11
【问题描述】:

为了让一个函数的单个实例一直运行,下面的方式在某个时候会不会有问题?

const Q = require('q');

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

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

【问题讨论】:

    标签: node.js promise ecmascript-6 q


    【解决方案1】:

    令人惊讶的是,不!我相信您的代码应该可以正常运行。

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

    JavaScript 中的几乎所有异步都以这种方式工作,包括 Q Promise 库。因此,在您的示例中,这是对正在发生的事情的(非常)简化的解释:

    • main() 被调用,创建一个新的堆栈帧。
    • Q.fcall(somePromiseFunction) 被调用,创建第二个堆栈帧(大概是第三个,当somePromiseFunction 被调用时。这会在后台触发异步操作,将传递给then 的函数设置为回调。
    • Q.fcall(somePromiseFunction) 现在已返回,因此这些堆栈帧已被清除。
    • main() 也到了末尾,所以它也被清除了 - 我们回到了一个空堆栈。
    • 异步操作完成后,会向事件队列推送一条消息,并与您的 then 回调关联。
    • 堆栈为空,因此事件循环开始处理。
    • 您的回调被执行。

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

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

    编辑: 为了回答您在 cmets 中的问题,我将澄清一些事情。最简单的递归函数如下所示:

    function recursive() {
        recursive();
    }
    

    调用一个函数会分配一个栈帧,当它返回时会被清除。然而,如果没有某种方式打破循环,像这个这样的递归函数永远不会返回 - 它会继续运行,无休止地分配堆栈帧,直到出现堆栈溢出错误。这并不是说你不应该在 JavaScript 中使用递归——它有时会非常有用!但是你需要注意,如果它递归太多而没有返回,你就会碰到栈顶。

    回答您的其他问题 - 事件循环/您的回调不会等待 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
    

    将此与我不断重复的功能进行比较:

    []
    [recursive]
    [recursive, recursive]            // Nothing stops the recursion,
    [recursive, recursive, recursive] // so the stack is never going to clear!
    

    【讨论】:

    • 是的,它似乎工作了很长时间。但我怀疑它是如何在第 5 行计算出 main() 在再次实际运行 main() 之前已经到达末尾的。如果堆栈帧总是被删除,那么递归函数在 JavaScript 中实际上是如何工作的。顺便说一句,感谢您提供信息丰富的答案。赞赏!
    • @omid.n:我编辑了我的答案以澄清您提出的一些问题。如果您需要更多信息,请告诉我:)
    猜你喜欢
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多