避免基于承诺的循环中的递归堆栈溢出?

时间:2015-05-05 13:49:06

标签: javascript node.js loops recursion promise

作为一个简单的示例程序,我有一个节点脚本连续ping服务器,并希望这个程序能够运行很长时间。

程序设置为ping函数,返回promise对象。根据ping是否有效来解决或拒绝承诺。

我希望这个函数在一个循环中运行,所以无论ping是否成功,下一次ping都会在上一个请求 后经过一段时间后触发解决。

问题本身不是这个任务,但我关心的是我的实施。我相信它最终会导致堆栈溢出。

这里有一些代码可以看看发生了什么:

function doPing(host) {
    // returns a promise object.
}

function doEvery(ms, callback, callbackArgs) {

    setTimeout(function() {

        callback.apply(null, callbackArgs)
            .always(function() {

                doEvery(ms, callback, callbackArgs);

            });

    }, ms);

}

doEvery(1000, doPing, [host]);

我试图限制代码只是为了反映以下问题的范围:

这会最终导致堆栈溢出吗? 是否有一种模式可以防止使用promises时基于回调的循环溢出?

1 个答案:

答案 0 :(得分:3)

这里没有堆栈溢出。 setTimeout是一个异步函数:它调度要运行的函数,但不会立即调用它。由于对doEvery的重复调用是在setTimeout的回调中,因此这将确保它不会溢出。

以下是最深层叠在不同点上的样子:

  • 安排第一次ping时:[global scope] -> doEvery -> setTimeout
  • 运行第一次ping时:[event loop] -> [handle timer] -> [closure #1 in doEvery] -> callback.apply -> doPing
  • 收到第一个回复时:[event loop] -> [handle network] -> promise.resolve -> [closure #2 in doEvery] -> doEvery -> setTimeout
  • 第二次超时到期时:[event loop] -> [handle timer] -> [closure #1 in doEvery] -> callback.apply -> doPing

正如您所看到的,每次等待承诺或超时时,控制权都会返回到事件循环中。当事件(例如达到超时或收到ping响应)时,事件循环将调用为该事件注册的回调。