node.js中ForEach中的异步请求

Ban*_*man 1 foreach asynchronous node.js requestjs

我是node.js(和request.js)的新手.我想从不同的路径特定URL获得一个网站的机身背部(在下面的例子中http://www.example.com/path1,http://www.example.com/path2等. )并使用键/值映射(下面的siteData [path])将此数据记录在对象中.

var request = require('request'),
    paths = ['path1','path2','path3'],
    siteData = {},
    pathLength = paths.length,
    pathIndex = 0;

paths.forEach((path) => {
    var url="http://www.example.com/"+path;
    request(url, function(error, response, html){
        if(!error){
            siteData[path] = response.body;
            pathIndex++;
            if(pathIndex===pathLength){
                someFunction(siteData);
            }
        }
});

function someFunction(data){
    //manipulate data
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • if语句(索引===长度)看起来不像是确定异步请求是否完成的正确方法.我该如何正确检查请求是否完成?
  • 当我执行上面的代码时,我得到一个错误,(node) warning: possible EventEmitter memory leak detected. 11 unpipe listeners added. Use emitter.setMaxListeners() to increase limit.我尝试链接,request(url, function(...){}).setMaxListeners(100);但这不起作用.

谢谢你的帮助!

Som*_*ens 10

看起来Promise是在这里完成工作的正确工具.我们将创建一个Promise在作业完成时解决的新对象,而不是回调.我们可以说"一旦你完成了,就做一些更多的东西"与.then运营商:

var rp = require('request-promise');

rp('http://www.google.com')
  .then((htmlString) => {
    // Process html... 
  });
Run Code Online (Sandbox Code Playgroud)

(如果出现任何问题,承诺拒绝并直接进入.catch)

someFunctionThatErrors('Yikes!')
  .then((data) => {
    // won't be called
  })
.catch((err) => {
  // Will be called, we handle the error here
});
Run Code Online (Sandbox Code Playgroud)

我们有很多异步任务要做,所以只有一个承诺不起作用.一种选择是将它们串联在一起,如下所示:

rp('http://www.google.com')
  .then((htmlString) => rp('http://someOtherUrl.com'))
  .then((otherHtmlString) => {
    // and so forth...
Run Code Online (Sandbox Code Playgroud)

但这会失去一些异常的异常 - 我们可以并行完成所有这些任务.

var myRequests = [];
myRequests.push(rp('http://www.google.com').then(processStuff).catch(handleErr));
myRequests.push(rp('http://someOtherUrl.com').then(processStuff).catch(handleErr));
Run Code Online (Sandbox Code Playgroud)

......男孩看起来很难看.所有这一切都有一个更好的方法 - Promise.all()(你使用箭头功能,所以我认为原生Promise也适合你).它接受一系列promise并返回一个promise,当所有数组的promise都完成执行时它将解析.(如果其中任何一个错误,它会立即拒绝).该.then函数将被赋予一个数组,表示每个promise所解析的值.

var myRequests = [];
myRequests.push(rp('http://www.google.com'));
myRequests.push(rp('http://someOtherUrl.com'));
Promise.all(myRequests)
  .then((arrayOfHtml) => {
    // arrayOfHtml[0] is the results from google,
    // arrayOfHtml[1] is the results from someOtherUrl
    // ...etc
    arrayOfHtml.forEach(processStuff);
  })
  .catch(/* handle error */);
Run Code Online (Sandbox Code Playgroud)

不过,我们必须手动调用.push我们想要访问的每个链接.那不行!让我们用一个漂亮的技巧来Array.prototype.map迭代我们的数组,依次操纵每个值并返回一个由新值组成的新数组:

var arrayOfPromises = paths.map((path) => rp(`http://www.example.com/${path}`));
Promise.all(arrayOfPromises)
  .then((arrayOfHtml) => arrayOfHtml.forEach(processStuff))
  .catch(function (err) { console.log('agh!'); });
Run Code Online (Sandbox Code Playgroud)

更清晰,更容易的错误处理.