因内存不足而无法完成承诺

时间:2016-07-25 17:14:11

标签: node.js memory-management promise es6-promise jsdom

我有一个脚本来刮掉~1000个网页。我使用Promise.all将它们组合在一起,并在所有页面完成后返回:

Promise.all(urls.map(url => scrap(url)))
    .then(results => console.log('all done!', results));

这是美好而正确的,除了一件事 - 由于并发请求,机器内存不足。我使用jsdom进行报废,它会快速占用几GB的内存,考虑到实例化数百个window,这是可以理解的。

我有一个想法,但我不喜欢它。也就是说,改变控制流程不使用Promise.all,但链接我的承诺:

let results = {};
urls.reduce((prev, cur) =>
    prev
        .then(() => scrap(cur))
        .then(result => results[cur] = result)
        // ^ not so nice. 
, Promise.resolve())
    .then(() => console.log('all done!', results));

这不如Promise.all ...不符合链接,并且必须存储返回的值以供以后处理。

有什么建议吗?我应该改进控制流程还是应该改进scrap()中的mem使用,还是有办法让节点限制内存分配?

1 个答案:

答案 0 :(得分:5)

您正尝试并行运行1000次网页抓取。您需要选择一些远小于1000的数字,并且一次只运行N,这样您在执行此操作时会消耗更少的内存。您仍然可以使用承诺来跟踪它们何时完成。

Bluebird's Promise.map()可以通过将并发值作为选项传递给您。或者,你可以自己写。

  

我有一个想法,但我不喜欢它。也就是说,改变控制   流不使用Promise.all,但链接我的承诺:

你想要的是N个同时在飞行中的操作。排序是一种特殊情况,其中N = 1通常比并行执行某些操作要慢得多(可能使用N = 10)。

  

这不如Promise.all ...不符合链接,   并且必须存储返回的值以供以后处理。

如果存储的值是您的内存问题的一部分,您可能必须以任何方式将它们存储在内存中。您将不得不分析存储结果使用的内存量。

  

有什么建议吗?我应该改进控制流程还是应该改进   在scrap()中使用mem,或者是否有办法让节点限制mem   分配

使用Bluebird's Promise.map()或自己写一些类似的东西。编写一些并行运行N个操作并保持所有结果有序的东西并不是火箭科学,但要做到这一点需要做一些工作。我之前在另一个答案中提出过它,但现在似乎无法找到它。我会继续寻找。

在此处找到我之前的相关答案:Make several requests to an API that can only handle 20 request a minute