nodejs循环中的多个http请求

IT技术 javascript node.js
2021-03-08 14:31:57

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

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')。它弄得一团糟,我不知道如何处理。

5个回答

我知道这是一个老问题,但我认为更好的解决方案是使用 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]
});
为什么会更好?一点解释真的会让它变得更好!
2021-04-27 14:31:57
自 2020 年 2 月 11 日起,请求已完全弃用。- github.com/request/request-promise
2021-04-28 14:31:57
您知道如果这些请求的响应类型NodeJS.ReadableStream|FileObject|Buffer是 watson 文本到语音 api 响应的类型,会发生什么情况吗?Promise.all()回调时响应是否完全“接收”和“完成” 当我尝试将每个可读流响应通过管道传输到forEach()回调中的Promise响应循环内的可写流中时,我得到不完整的文件。
2021-05-02 14:31:57
@MdSifatulIslam 这使用 es6,所以它可能对每个人都没有帮助。一旦您习惯了这种 [现代] 编码风格,它就会更加简洁、高效且易于消化。
2021-05-14 14:31:57
我喜欢这种方法。干净多了
2021-05-16 14:31:57

默认情况下,节点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 请求并且它们必须按顺序执行怎么办..我试过 async /await 但它们是并行执行的...我的意思是顺序是,在完成一组 http 请求之后,它应该转到下一个http请求数组。Async/await同时执行所有HTTP请求数组
2021-04-30 14:31:57
Adrian 的评论非常重要。请注意,此回答并不意味着这些请求中的任何一个已完成通过此解决方案,您只知道请求已“发送”并且迭代已结束。
2021-05-12 14:31:57
不应该是“http.get(urls[url]...”或者在那种情况下“for (url of urls)”?假设 urls 是一个数组。
2021-05-12 14:31:57
谢谢,它激励我按照你说的方式解决这个问题。
2021-05-14 14:31:57
实际上,按照现在的方式,它会在发送最后一个请求时说它已完成,但不会在所有请求都完成时说。我给出了我最终为解决这个问题所做的事情的答案。
2021-05-19 14:31:57

您需要检查 on end(data complete event) 已被称为请求的确切数量...这是一个工作示例:

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());
      }      
    });
  });
})
很有帮助的例子。谢谢
2021-04-23 14:31:57
使用请求module而不是 http 会如何工作?我的意思是可以使用请求和异步module来实现这一点吗?
2021-04-24 14:31:57
@Adrian 不应该 var Responds = []; 在 forEach 之外而不是在里面声明?
2021-05-05 14:31:57
@blue-sky 不,因为我想将每个链接的响应分开。forEach 循环将从第一个链接开始,data并将推送多个块。end将显示完整的响应。在下一个链接上,我想从一个空的 开始response,因为我已经注销了响应。
2021-05-21 14:31:57

您可以使用任何具有“.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);

使用闭包可以很容易地解决这个问题。创建一个函数来处理请求并在循环中调用该函数。每次调用该函数时,它都有自己的词法作用域,并且使用闭包,即使循环结束,它也能够保留 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))
}