如何避免基于 Promise 的循环中出现递归堆栈溢出问题?

6
作为一个简单的示例程序,我有一个节点脚本可以连续ping服务器,并希望该程序长时间运行。
该程序设置为ping函数,返回一个promise对象。根据ping是否成功或失败,该promise将被解析或拒绝。
我希望在循环中运行此函数,因此无论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 时,是否有一种模式可以防止回调循环过程中的溢出?


抱歉出现了重复的问题 - 相关问题在最初的搜索或撰写问题时并未出现。 我认为这个问题更具体。链接的问题是基于观点的,而我尽可能地将这个问题保持客观和特定于基于 Promise 的循环。 - Sean
1个回答

4
这里没有堆栈溢出。 setTimeout 是一个异步函数:它安排函数运行,但不立即调用它。由于对 doEvery 的重复调用在 setTimeout 的回调函数中,这将确保它不会溢出。
以下是各个时刻最深的堆栈示例:
当安排第一个 ping 时:[全局范围] -> doEvery -> setTimeout 当运行第一个 ping 时:[事件循环] -> [处理计时器] -> [闭包#1在doEvery中] -> callback.apply -> doPing 当第一个响应被接收时:[事件循环]->[处理网络]->promise.resolve -> [闭包#2 in doEvery] -> doEvery - > setTimeout 当第二个超时到期时:[事件循环] -> [处理计时器] -> [闭包#1在doEvery中] -> callback.apply -> doPing 所以,正如您所看到的,每次等待承诺或超时时,控制权都会返回事件循环。当事件(例如达到超时或接收到ping响应)时,事件循环随后将调用为该事件注册的回调。

谢谢。我担心异步函数仍会导致堆栈增长。 - Sean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接