Joris Ooms
Joris Ooms

Reputation: 12048

Promise returns early with a for loop

I'm new to Promises so I might be doing something stupid here, but I can't seem to figure it out.

Just so I know I'm on the right path, a bit of information upfront. I have an authenticate method which returns a promise:

APIWrapper.prototype.authenticate = function() {
  var self = this;
  return new Promise(function(resolve, reject) {
    request({
      uri: self.httpUri + '/auth/authenticate',
      method: 'GET',
      headers: {
        auth_user: self.user,
        auth_pass: self.pass,
        auth_appkey: self.appkey
      }
    }, function(err, res, body) {
      if (err) return reject(err);
      self.parser.parseXML(body, function(err, result) {
        if (err) return reject(err);
        if (result.error) { return reject(result.error) }
        self.token = result.auth.token[0];
        return resolve(result);
      });
    });
  });
};

I chain this with .getDashboards() like this:

wrapper.authenticate().then(function() {
  wrapper.getDashboards();
}).then(function(result) {
  console.log('result', result);
});

.getDashboards() also returns a promise:

APIWrapper.prototype.getDashboards = function() {
  var self = this;
  return new Promise(function(resolve, reject) {
    request({
      url: self.httpUri + '/user/dashboard',
      method: 'GET',
      headers: {
        auth_appkey: self.appkey,
        auth_token: self.token
      }
    }, function(err, res, body) {
      if (err) { return reject('Could not connect to the API endpoint'); };
      self.parser.parseXML(body, function(err, data) {
        var dashboards = [];
        if(err) { return reject(err); }
        if(data.error) { return reject(data.error); }
        for(var i = 0; i < data.Dashboards.Dashboard.length; i++) {
          dashboards.push(self.getDashboard(data.Dashboards.Dashboard[i]));
        }
        // returns early here
        resolve(dashboards);
      });
    });
  });
};

With the .getDashboard() method like this at the moment:

APIWrapper.prototype.getDashboard = function(db) {
  var dashboard = {};
  dashboard.title = db.Title[0];
  dashboard.id = db.$.id;
  console.log(dashboard);
  return dashboard;
};

What happens with this code is that it returns the result before it returns the dashboards. I suspect the resolve() in .getDashboards() doesn't wait for the for loop to finish? Do I need to use promises in the .getDashboard() method as well, or how would I wait for it to finish before resolving my .getDashboards() promise?

Output:

> result undefined
{ title: 'Dashboard 1', id: '3271' }
{ title: 'Dashboard 2', id: '3272' }
{ title: 'Dashboard 3', id: '3273' }

I'm using this Promise implementation at the moment: https://github.com/then/promise

Upvotes: 3

Views: 159

Answers (1)

Denys S&#233;guret
Denys S&#233;guret

Reputation: 382444

You need to return the promise to have it chained :

wrapper.authenticate().then(function() {
  return wrapper.getDashboards();
}).then(function(result) {
  console.log('result', result);
});

In your case, it can be simplified as

wrapper.authenticate()
.then(wrapper.getDashboards)
.then(function(result){
  console.log('result', result);
});

You also don't seem to handle errors. The then library seems very raw on this point, so you should probably add a second argument :

wrapper.authenticate()
.then(wrapper.getDashboards, onAuthenticateError)
.then(function(result){
  console.log('result', result);
}, onDashboardError);

Upvotes: 2

Related Questions