【问题标题】:nodejs multiple http requests in loopnodejs循环中的多个http请求
【发布时间】:2013-11-23 13:18:16
【问题描述】:

我正在尝试在 node 中制作简单的提要阅读器,但我在 node.js 中遇到了多个请求的问题。 例如,我得到了带有类似网址的表格:

urls = [
"http://url1.com/rss.xml",
"http://url2.com",
"http://url3.com"];

现在我想获取每个 url 的内容。第一个想法是使用for(var i in urls),但这不是一个好主意。最好的选择是异步执行,但我不知道如何实现。

有什么想法吗?

编辑:

我得到了这个代码:

var data = [];
for(var i = 0; i<urls.length; i++){
    http.get(urls[i], function(response){
    console.log('Reponse: ', response.statusCode, ' from url: ', urls[i]);
    var body = '';
    response.on('data', function(chunk){
        body += chunk;
    });

    response.on('end', function() {
        data.push(body);
    });
}).on('error', function(e){
    console.log('Error: ', e.message);
});
}

问题是,首先是循环中每个元素的调用行“http.get...”,然后调用事件 response.on('data'),然后调用 response.on('end')。它弄得一团糟,我不知道如何处理。

【问题讨论】:

  • 问题是什么?你想达到什么目的?
  • 不要在数组上使用for (x in y)!它不会像你想的那样!使用for (idx=0,len=array.length; idx &lt; len; ++idx){value=array[idx]; ....}
  • 在一个循环中异步获取每个url的内容
  • 这是一个重复的问题,在堆栈溢出时回答了很多次。

标签: javascript node.js


【解决方案1】:

Promise.allSettled 不会因错误而停止。它确保您处理所有响应,即使有些响应有错误。

Promise.allSettled(promises)
 .then((data) => {
// do your stuff here
 })
 .catch((err) => {
   console.log(JSON.stringify(err, null, 4));
 });

【讨论】:

    【解决方案2】:

    使用闭包可以轻松解决问题。创建一个函数来处理请求并在循环中调用该函数。每次调用该函数时,它都有自己的词法范围,并且使用 closure,即使循环结束,它也能够保留 URL 的地址。即使响应是在中,关闭也可以处理这些东西。

    const request = require("request");
    
    function getTheUrl(data) {
        var options = {
            url: "https://jsonplaceholder.typicode.com/posts/" + data
        }
        return options
    }
    
    function consoleTheResult(url) {
        request(url, function (err, res, body) {
            console.log(url);
        });
    }
    
    for (var i = 0; i < 10; i++) {
        consoleTheResult(getTheUrl(i))
    }
    

    【讨论】:

      【解决方案3】:

      我知道这是一个老问题,但我认为更好的解决方案是使用 JavaScripts Promise.all():

      const request = require('request-promise');
      const urls = ["http://www.google.com", "http://www.example.com"];
      const promises = urls.map(url => request(url));
      Promise.all(promises).then((data) => {
          // data = [promise1,promise2]
      });
      

      【讨论】:

      • 为什么会更好?一点解释真的会让它变得更好!
      • 我喜欢这种方法。干净多了
      • @MdSifatulIslam 这使用 es6,所以它可能对每个人都没有帮助。一旦您习惯了这种 [现代] 编码风格,它就会更加简洁、高效且易于理解。
      • 你知道如果这些请求的响应类型是 NodeJS.ReadableStream|FileObject|Buffer 类型会发生什么,就像 watson 文本到语音 api 响应的情况一样?在Promise.all() 回调时,响应是否完全“收到”和“完成”?当我尝试通过回调中的承诺响应将每个可读流响应通过管道传递到forEach() 循环中的可写流中时,我得到不完整的文件。
      • 自 2020 年 2 月 11 日起,请求已完全弃用。 - github.com/request/request-promise
      【解决方案4】:

      您可以使用任何带有“.all”实现的 Promise 库。我用的是 RSVP 库,很简单。

      var downloadFileList = [url:'http://stuff',dataname:'filename to download']
      var ddownload = downloadFileList.map(function(id){
                var dataname = id.dataname;
                var url = id.url;
                return new RSVP.Promise(function(fulfill, reject) {
                 var stream = fs.createWriteStream(dataname);
                  stream.on('close', function() {
                  console.log(dataname+' downloaded');
                  fulfill();  
                  });
                request(url).on('error', function(err) {
          console.log(err);
          reject();
        }).pipe(stream);
              });
              });      
              return new RSVP.hashSettled(ddownload);
      

      【讨论】:

        【解决方案5】:

        默认情况下,节点http 请求是异步的。您可以在代码中按顺序启动它们并调用将在所有请求完成后启动的函数。您可以手动完成(计算已完成与已启动的请求)或使用 async.js

        这是不依赖的方式(省略错误检查):

        var http = require('http');    
        var urls = ["http://www.google.com", "http://www.example.com"];
        var responses = [];
        var completed_requests = 0;
        
        for (i in urls) {
            http.get(urls[i], function(res) {
                responses.push(res);
                completed_requests++;
                if (completed_requests == urls.length) {
                    // All download done, process responses array
                    console.log(responses);
                }
            });
        }
        

        【讨论】:

        • 谢谢,它启发了我按照你说的方式解决这个问题。
        • 其实就是现在的样子,当最后一个请求发送时它会说它完成了,但当所有请求都完成时就不会。我回答了我最终为解决这个问题所做的事情。
        • 阿德里安的评论很重要。请注意,此答案并不意味着这些请求中的任何一个已完成。通过这个解决方案,您只知道请求被“发送”并且迭代结束。
        • 不应该是“http.get(urls[url]...”还是那种情况下的“for (url of urls)”?假设urls是一个数组。
        • 如果我想用数组执行多个 http 请求并且它们必须按顺序执行..我尝试 async /await 到但它们是并行执行的...我的意思是按顺序执行之后一个http请求数组完成,应该转到下一个http请求数组。async/await同时执行所有http请求数组
        【解决方案6】:

        您需要检查在end(数据完成事件)上调用的请求的确切数量...这是一个工作示例:

        var http = require('http');
        var urls = ['http://adrianmejia.com/atom.xml', 'http://twitrss.me/twitter_user_to_rss/?user=amejiarosario'];
        var completed_requests = 0;
        
        urls.forEach(function(url) {
          var responses = [];
          http.get(url, function(res) {
            res.on('data', function(chunk){
              responses.push(chunk);
            });
        
            res.on('end', function(){
              if (completed_requests++ == urls.length - 1) {
                // All downloads are completed
                console.log('body:', responses.join());
              }      
            });
          });
        })
        

        【讨论】:

        • 使用 request 模块而不是 http 如何工作?我的意思是可以使用请求和异步模块来实现吗?
        • @Adrian 不应该 var response = [];在 forEach 外部而不是内部声明?
        • @blue-sky 不,因为我想将每个链接的响应分开。 forEach 循环将从第一个链接开始,data 将推送多个块。 end 将显示完整的回复。在下一个链接中,我想从一个空的response 开始,因为我已经退出了响应。
        • 非常有用的例子。谢谢
        猜你喜欢
        • 1970-01-01
        • 2017-06-28
        • 2016-08-24
        • 2021-11-07
        • 1970-01-01
        • 1970-01-01
        • 2017-04-03
        • 2020-05-09
        • 1970-01-01
        相关资源
        最近更新 更多