带有异步请求的 NodeJS

Posted

技术标签:

【中文标题】带有异步请求的 NodeJS【英文标题】:NodeJS with Async requests 【发布时间】:2013-10-23 12:35:07 【问题描述】:

我遇到了对于 Node JS 初学者来说似乎是一个传统问题,以及异步请求。

我有未知数量的 URL 由用户生成,随后存储在我的 Node JS 服务器上的一个数组中。 Node JS 服务器必须遍历这些 URL,依次向每个 URL 发出请求。它必须按顺序这样做,并且必须等待每个响应才能移动到下一个 URL(当将发出新请求时)。最终结果应该是所有响应的有序集合(恰好是 JSON),作为 JSON 对象很好地存储在一起,准备好后又可以发送回客户端。

我认为我应该使用 async NodeJS 库,并且我已经在使用 needle 来发出请求。

URLs = ["http://a", "http://s", "http://d"];
async.eachSeries(URLs, function (URL, callback)  ..... );

我不清楚如何使用异步来确保 Needle 请求已完成,并在转到下一个 URL 请求之前相应地存储该响应。下面是我的 Needle 请求示例。

 needle.get(URL, options, function(error, response, body) ... );

欢迎对整个问题提供部分或完整的解决方案。

【问题讨论】:

我在这里使用了递归方法:***.com/a/6048595/2063246(非常适合我) 我会尽可能推荐 promises 【参考方案1】:

有了 Promise,你可以做到这一点:

var Promise = require("bluebird");
var get = Promise.promisify(needle.get, needle);

var URLs = ["http://a", "http://s", "http://d"];
var current = Promise.fulfilled();
Promise.map(URLs, function (URL) 
    current = current.then(function () 
        return get(URL);
    );
    return current;
).map(function(responseAndBody)
    return JSON.parse(responseAndBody[1]);
).then(function (results) 
    console.log(results);
).catch(function (e) 
    console.error(e);
);

另外,当网站的 json 无效或以错误消息/空正文响应时,您的服务器不会崩溃。手动编写时,您需要手动尝试捕获,但承诺会处理catch() 中的两种错误。由于 url 是由用户提供的,如果您不将手动 try-catch 添加到 non-promise 代码,他们可以轻松地拒绝您的服务器。

【讨论】:

感谢您提供此解决方案。我会试一试。我将柏拉图的回答标记为“正确”,因为它坚持我在问题中提到的库。它们是否是这项工作的最佳工具肯定有待商榷。如果我可以将两者都标记为正确,我会的。 @jtromans 当然。顺便说一句,如果您无法按预期工作,请告诉我。 @Esailija 你设置var current = Promise.fulfilled() 并使用它Promise.map 的任何原因?我们可以在第一个地图函数中使用return get(URL) 吗?【参考方案2】:

这里有两个例子,一个用async.eachSeries逐一保存结果,一个用async.mapSeries收集所有结果然后一次性保存

URLs = ["http://a", "http://s", "http://d"];
function iterator1(URL, done)
  var options = ;
  needle.get(URL, options, function(error, response, body) 
    if(error) return done(error) ;
    processAndSaveInDB(body, function(err)
      if(err) return done(err) ;
      done(null);
    );
  );
;

async.eachSeries(URLs
, iterator1
, function (err)
  // global callback for async.eachSeries
  if(err) 
    console.log(err) 
   else 
    console.log('All Needle requests successful and saved');
  
);

// Here is a similar technique using async.map, it may be more suitable
function iterator2(URL, done)
  var options = ;
  needle.get(URL, options, function(error, response, body) 
    if(error) return done(error) ;
    done(null, body);
  );
;

async.mapSeries(URLs
, iterator2
, function (err, results)
  // global callback for async.mapSeries
  if(err) 
    console.log(err) 
   else 
    console.log('All Needle requests successful');
    // results is a 1 to 1 mapping in order of URLs > needle.body
    processAndSaveAllInDB(results, function(err)
      if(err) return done(err) ;
      console.log('All Needle requests saved');
      done(null);
    );
  
);

我不清楚如何使用 async 来确保 Needle 请求已完成,并在移动到下一个 URL 请求之前相应地存储该响应。

异步函数的series 变体负责处理这个问题;您只需确保在准备好继续之前不要调用迭代器函数的 done 回调。在实践中,这意味着将调用 done 放在最里面的回调中(例如您的 Needle 回调)

【讨论】:

重新阅读您的问题后,mapSeries 解决方案可能更有用;你会用concatenateJSONResults(可能是同步的)替换我的虚拟processAndSaveAllInDB,然后发送你的回复 两者都试过了,会选择 mapSeries 选项。非常感谢。当我使用倒数函数来实现类似类型的结果时,我还有其他一些范例,因此这种方法将非常有用。

以上是关于带有异步请求的 NodeJS的主要内容,如果未能解决你的问题,请参考以下文章

使用 nodejs 异步和请求模块

使用异步和请求包(NodeJS / Express)进行多个 API 调用

nodejs中的多个异步mongo请求

如何从请求中返回结果,以及在 NodeJS 中执行异步任务但没有回调之后

带有 Python 请求的异步请求

js异步请求发展史和yield