对多个节点请求使用promises

时间:2015-09-08 17:21:27

标签: node.js express promise

使用request库,有没有办法使用promises来简化这个回调?

  var context = {};

  request.get({
    url: someURL,
  }, function(err, response, body) {

    context.one = JSON.parse(body);

    request.get({
      url: anotherURL,
    }, function(err, response, body) {
      context.two = JSON.parse(body);

      // render page
      res.render('pages/myPage');
    });
  });

5 个答案:

答案 0 :(得分:4)

这是使用Bluebird promises库的解决方案。这会对两个请求进行序列化并将结果累积到context对象中,并将错误处理汇总到一个地方:

var Promise = require("bluebird");
var request = Promise.promisifyAll(require("request"), {multiArgs: true});

var context = {};
request.getAsync(someURL).spread(function(response, body) {
    context.one = JSON.parse(body);
    return request.getAsync(anotherURL);
}).spread(response, body)
    context.two = JSON.parse(body);
    // render page
    res.render('pages/myPage');
}).catch(function(err) {
    // error here
});

并且,如果您有多个网址,则可以使用蓝鸟等其他一些功能(如Promise.map())来迭代一系列网址:

var Promise = require("bluebird");
var request = Promise.promisifyAll(require("request"), {multiArgs: true});

var urlList = ["url1", "url2", "url3"];
Promise.map(urlList, function(url) {
    return request.getAsync(url).spread(function(response,body) {
        return [JSON.parse(body),url];
    });
}).then(function(results) {
     // results is an array of all the parsed bodies in order
}).catch(function(err) {
     // handle error here
});

或者,您可以创建一个辅助函数来为您执行此操作:

// pass an array of URLs
function getBodies(array) {
    return Promise.map(urlList, function(url) {
        return request.getAsync(url).spread(function(response.body) {
            return JSON.parse(body);
        });
    });
});

// sample usage of helper function
getBodies(["url1", "url2", "url3"]).then(function(results) {
    // process results array here
}).catch(function(err) {
    // process error here
});

答案 1 :(得分:2)

以下是我将如何实现链式Promises。



var request = require("request");

var someURL = 'http://ip.jsontest.com/';
var anotherURL = 'http://ip.jsontest.com/';

function combinePromises(context){
  return Promise.all(
    [someURL, anotherURL].map((url, i)=> {

      return new Promise(function(resolve, reject){

        try{

          request.get({
            url: url,
          }, function(err, response, body) {

            if(err){
              reject(err);
            }else{
              context[i+1] = JSON.parse(body);
              resolve(1); //you can send back anything you want here
            }

          });

        }catch(error){
          reject(error);
        }

      });

    })
  );
}

var context = {"1": "", "2": ""};
combinePromises(context)
.then(function(response){

  console.log(context);
  //render page
  res.render('pages/myPage');
  
}, function(error){
  //do something with error here
});




答案 2 :(得分:1)

使用原生Promises执行此操作。理解胆量很好。

  

@Bergi在评论中指出这里称为“Promise Constructor Antipattern”。不要这样做。看看下面更好的方法。

var contextA = new Promise(function(resolve, reject) {
  request('http://someurl.com', function(err, response, body) {
    if(err) reject(err);
    else {
      resolve(body.toJSON());
    }
  });
});

var contextB = new Promise(function(resolve, reject) {
  request('http://contextB.com', function(err, response, contextB) {
    if(err) reject(err);
    else {
      contextA.then(function(contextA) {
         res.render('page', contextA, contextB);
      });
    }
  });
});

这里有一个漂亮的技巧,我认为通过使用原始承诺,你会明白这一点,contextA解析一次然后我们可以访问它的解析结果。这是,我们从不someurl.com提出上述请求,但仍然可以访问contextA的JSON。

所以我可以想象创建一个contextC并重用JSON而不必另外发出请求。 Promises始终只解析一次。您必须将匿名执行程序函数取出并将其放在new Promise中以刷新该数据。

奖金说明:

这会并行执行contextAcontextB,但会在AB时同时进行需要两种情境的最终计算。 promisifying已解决。

  

这是我对此的新尝试。

上述解决方案的主要问题是承诺可重用并且它们不是链接,这是Promises的一个关键特性。

但是,我仍然建议request您自己的promisifying库,并且不要在项目中添加其他依赖项。 rejection自己的另一个好处是你可以编写自己的API逻辑。如果您正在使用在正文中发送error条消息的特定//Function that returns a new Promise. Beats out constructor anti-pattern. const asyncReq = function(options) { return new Promise(function (resolve, reject) { request(options, function(err, response, body) { //Rejected promises can be dealt with in a `catch` block. if(err) { return reject(err); } //custom error handling logic for your application. else if (hasError(body)) { return reject(toError(body)); } // typically I just `resolve` `res` since it contains `body`. return resolve(res); } }); }; asyncReq(urlA) .then(function(resA) { //Promise.all is the preferred method for managing nested context. return Promise.all([resA, asyncReq(urlB)]); }) .then(function(resAB) { return render('page', resAB[0], resAB[1]); }) .catch(function(e) { console.err(e); }); ,这一点非常重要。我们来看看:

while (cur->next != NULL)

答案 3 :(得分:1)

您可以使用request-promise库来执行此操作。在您的情况下,您可能会遇到类似这样的问题,您可以在其中链接请求。

request
    .get({ url: someURL })
    .then(body => {
        context.one = JSON.parse(body);

        // Resolves the promise
        return request.get({ url: anotherURL });
    })
    .then(body => {
        context.two = JSON.parse(body);
        res.render('pages/myPage');
    })
    .catch(e => {
        //Catch errors
        console.log('Error:', e);
    });

答案 4 :(得分:0)

到目前为止最简单的方法是使用request-promise库。您还可以使用bluebird之类的promise库并使用其promisify函数将request回调API转换为promise API,但您可能需要将自己的promisify函数编写为{ {1}}不使用标准回调语义。最后,您可以使用本机承诺或蓝鸟来制作自己的承诺包装器。

如果您刚刚开始,请使用请求承诺。如果您正在重构现有代码,我会使用bluebird的request函数为request编写一个简单的包装器。