javascriptnode.jsexpresspromisebluebird

Nested async requests using request-promise


I'm working with the Visual Studio Online API and trying to get branch stats by repository. To do this I've nested my async calls. I'm using request-promise to resolve my GET requests.

The issue I'm having is understanding how to return the model after all the branches have been added to the top-level model:

When I console.log the result I get [] obviously since it isn't resolving the branch requests.

var model = [];
rp(options)
  .then(function(repos) {
    repos.value.forEach(function(repository) {
      var repo = {
        id: repository.id,
        name: repository.name,
        branches: []
      };

      var branchOptions = options;
      branchOptions.url = config.endPoints.base + config.endPoints.branches(repository.id);

      rp(branchOptions)
        .then(function(branches) {
          branches.value.forEach(function(branch) {
            repo.branches.push({
              name: branch.name,
              behind: branch.behindCount,
              ahead: branch.aheadCount
            });
          })
        }).then(function() {
          model.push(repo);
        });
    });
  }).finally(function(){
        console.log(model);
        res.json(model);
  });

I tried to add a .then() after the foreach but obviously a forEach doesn't return a promise.

Any ideas? I've been programming for 14 hours so nothing is making sense to me haha.


Solution

  • The below should fix up your issue, instead of doing forEach loops i've replaced this with a .map() in your promise chain instead. Also I've done this within your inner promise. Additionally, I've made the inner promise return upon completion so the outer map knows when this has completed for each iteration.

    I left the .finally() because that indicates that we will always want to respond to the user regardless of the outcome of populating model.

    I would also recommend adding .catch() to both your outer and inner promises to make sure that you're handling any errors appropriately. As it stands if an error occurs nothing will handle it and model will be returned and you'll never know an error occurred on one of your iterations with the .map() on the inner or outer promises.

    Also its worth noting that request-promise implements A+ Promises using bluebird.

    var model = [];
    rp(options)
      .map(function(repository) {
        var repo = {
          id: repository.id,
          name: repository.name,
          branches: []
        };
    
        var branchOptions = options;
        branchOptions.url = config.endPoints.base + config.endPoints.branches(repository.id);    
    
        return rp(branchOptions)
          .map(function(branch){ 
            repo.branches.push({
              name: branch.name,
              behind: branch.behindCount,
              ahead: branch.aheadCount
            });
          })
          .then(function() {
            model.push(repo);
          });
      })
      .finally(fuction() {
        console.log(model);
        return res.json(model);
      });