避免在基于promise的循环中发生recursion堆栈溢出?
作为一个简单的例子程序,我有一个连续ping服务器的节点脚本,希望这个程序长时间运行。
程序设置为一个返回一个promise对象的ping函数。 这个承诺是根据ping是工作还是失败来解决或拒绝的。
我希望这个函数在一个循环中运行,所以无论ping是否成功,在前一个请求被parsing之后的一段时间之后 ,下一个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]);
我试图限制代码只是为了反映以下问题的范围:
这最终会导致堆栈溢出? 有一种模式可以防止使用promise的基于callback的循环溢出吗?
这里没有堆栈溢出。 setTimeout
是一个asynchronous函数:它调度要运行的函数,但不立即调用它。 由于doEvery
的重复调用在setTimeout
的callback中,这将确保它不会溢出。
这里是一个例子,最深的堆栈可能在不同的点上看起来像:
- 调度第一个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响应)时,事件循环会调用为该事件注册的callback。