简体   繁体   中英

Looping through x after all i : node.js

I have this code. I want to loop through all Users in a database and for each user get a list of their Portfolios (which in turn is a collection of stocks):

 calculatePortfolios: function(callback) {
    var thisuser;
    module.exports.getAllUsers(function(err, users) {
      /* Loop through all users with i */
      for(var i = 0; i < users.length; i++) {
         console.log('i = ' + i);
         console.log(users.length);
         thisuser = users[i]; // Store current user.

         /* Loop through all held stocks in current user's portfolio. */
         module.exports.getPortfolio(thisuser.username, function(err, portfolio) {
            for(var x = 0; x < portfolio.length; x++) {
               console.log('x = ' + x);
               var total = parseFloat((portfolio[x].held * portfolio[x].unitprice) +  thisuser.cash);
               console.log(total);
               console.log(thisuser.username);
               module.exports.updateInvestor(thisuser.username, total, function(err, result) {
                   console.log(thisuser.username + ' ' + total);
               });
           }
      });
     }
     callback(err, 'OK');
  });
},

The result I get is that all i indices (users) are looped through before all x indices (portfolios). Shouldn't x be an inner loop of i?

Is this something related to how Node.JS works?

Any help is much appreciated. Thank you.

the getPortfolio() is most likely an asynchronous operation, and depending on the internal implementation it may either queue the calls or translate them to http requests for example that may take time, when these requests are completed only then your callback function that operates with x will be called. But meanwhile getPortfoli() call will return. That is exactly why you see it iterating fast over all your i's, and only then you callback starts getting called.

Lack of JOIN 's is really confusing to users who come with SQL background to node.js (event-driven) and NoSQL databases.

Consider this flow for your task:

  1. Load all users.
  2. Iterate through each user and record field which is used for collection relationship (username in your case), usually it is _id. 2b. (Optional) make Object, where key - will be field of relationship, in your case username , and value - will be object it self.
  3. Make another query to related collection with similar query: { _id: { $in: ids } } where ids will be array of user _id's. In your case you need list of usernames.
  4. Iterate through each portfolio item and add it to user object. If 2b did - then no need of two iterations.
  5. Users are done, and have portfolio data and ready to be used.

@PSL: Got it working with async.waterfall . Probably not the most elegant solution, but it works for now.

 /**
  * @description Calculate the value of an investor's portfolio.
  * @function
 */
 calculatePortfolios: function(callback) {
     async.waterfall([
       function(callback) {
         var allUsers = [];
         module.exports.getAllUsers(function(err, users) {
           for(var i = 0; i < users.length; i++) {
             allUsers.push(users[i]);
           } 
         });
         callback(null, allUsers);
       },
       function(allUsers, callback) {
         var totals = [];
         for(var i = 0; i < allUsers.length; i++) {
           module.exports.getPortfolio(allUsers[i].username, function(err, portfolio) {
             for(var x = 0; x < portfolio.length; x++) {
               totals.push(parseFloat(portfolio[x].held * portfolio[x].unitprice));
             }
           });
         }
         callback(null, allUsers, totals);
      },
      function(allUsers, totals, callback) {
        for(var i = 0; i < allUsers.length; i++) {
          module.exports.updateInvestor
          (allUsers[i].username, (totals[i] + allUsers[i].cash), function(err, result) {
            console.log('Updated an investor!');
          });
        }
        callback(null, 'OK');
      }
   ]);
   callback(null, 'OK');
  },

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM