我正在从NodeJS中的GET URL API中抓取一堆东西。我正在环绕一年中的X个城市。我有一个scrapeChunk()函数,我为每个参数实例调用一次,即{startDate:...,endDate:...,location:...}。在里面我对表进行jsdom解析,转换为CSV,将CSV附加到文件中。在所有嵌套的异步回调中,我最终使用下一个参数实例再次调用scrapeChunk函数。
一切正常,但节点实例在RAM中增长并增长,直到我收到“致命错误:CALL_AND_RETRY_2分配失败 - 处理内存不足”错误。
我的问题:我做错了什么,或者这是JavaScript和/或我正在使用的库的限制?我可以以某种方式让每个任务完成,释放它的内存,然后开始下一个任务吗?我尝试了一个来自FuturesJS的序列,它似乎遭受了同样的泄漏。
答案 0 :(得分:4)
可能发生的事情是你正在构建一个非常深的调用树,并且它的上层保持对它们的数据的引用,所以它永远不会被垃圾收集器声明。
要做的一件事是,在你自己的代码中,当你在最后调用一个回调时,通过调用process.nextTick()来做到这一点。这样,调用函数可以结束并释放其变量。此外,请确保您没有将所有数据堆积到一个全局结构中,以便永久保存这些引用。
如果没有看到代码,那么提出好的回复就有点棘手了。但这不是node.js或其方法的限制(有许多使用它的长期运行和复杂的应用程序),但是如何使用它。
答案 1 :(得分:2)
您可能想尝试cheerio而不是JSDom。作者声称它更精简,速度提高了8倍。
答案 2 :(得分:1)
假设你的描述是正确的,我认为问题的原因是显而易见的 - 对scrapeChunk()的递归调用。使用循环调度任务(或查看节点的流设施),并确保它们实际返回。
这里发生的事情听起来像这样:
var list = [1, 2, 3, 4, ... ];
function scrapeCheck(index) {
// allocate variables, do work, etc, etc
scrapeCheck(index+1)
}
使用足够长的列表,您可以保证耗尽内存,堆栈深度,堆或任何数量的内容,具体取决于您在函数体中执行的操作。我建议的是这样的:
var list = [1, 2, 3, 4, ... ];
list.forEach(function scrapeCheck(index) {
// allocate variables, do work, etc, etc
return;
});
令人沮丧的嵌套回调是一个正交问题,但我建议你看一下async库(特别是async/waterfall),这对于这类任务来说既流行又有用。 / p>
答案 3 :(得分:0)
这与对函数的递归调用有关。将递归调用放在一个
setTimeout(()=>{
recursiveScrapFunHere();
}, 2000);
通过这种方式,调用是异步的,并被放置在优先级堆中,而不是通常的递归堆栈中(同步调用就是这种情况)。
这样你的父函数(同一个函数)会一直运行到最后,并且 recursiveScrapFunHere() 在递归堆栈之外。
此处将在延迟 2 秒后进行呼叫。