简体   繁体   中英

How properly handle async functions in nodejs

I am new to nodejs, and I don't properly understand how async functions works. I read about them a lot today, but I cant solve my problem.

I use Sequelize.js as the ORM and my problem is when I nest a query into the callback of an other query then I cant force it to continues only when both query ended.

Here is my current code:

io.on('connection', function (socket) {
  socket.on('join', function (data) {
    clients[clients.length] = new Client("Client " + clients.length, data.channel);
    console.log('Client connected Channel: ' + clients[clients.length-1].channel);
    var array = []
    DB.Matches.findAll({attributes: ['matchId', 'teamAId', 'teamBId']}).then(function (result) {
      for (var i = result.length - 1; i >= 0; i--) {
        DB.Teams.findAll({where: { team_id: [result[i].teamAId,result[i].teamBId]}}).then(function (teams) {
          array.push({ id: 0, name: teams[0].clubName + ' - ' + teams[1].clubName});         
        }).then(function () {
          // Now my emit event is here but I dont want to run every time the  loop run
          console.log(array);
          socket.emit('matches', array); 
        });
      }
    }.then(function () {
      // I tried to put it here, but then I got an empty array, because the queries haven't finshed yet 
    }));
  });
});

When this code is called, the array will be emited in every loop with one more element in it in every loop, but this is not good for me. I want to call the emit event once when the array is totally filled.

The preferred way of solving this kind of thing is to use Promise.all

io.on('connection', function (socket) {
  socket.on('join', function (data) {
    clients[clients.length] = new Client("Client " + clients.length, data.channel);
    console.log('Client connected Channel: ' + clients[clients.length-1].channel);
    DB.Matches.findAll({attributes: ['matchId', 'teamAId', 'teamBId']}).then(function (result) {
      var promises = [];
      for (var i = result.length - 1; i >= 0; i--) {
        promises.push(
          DB.Teams.findAll({where: { team_id: [result[i].teamAId,result[i].teamBId]}}).then(function (teams) {
             return { id: 0, name: teams[0].clubName + ' - ' + teams[1].clubName};         
          }));
      }
      Promise.all(promises).then(function(array) {
          console.log(array);
          socket.emit('matches', array); 
        });
    });
  });
});

edit:

If I understand you correctly you want to write return { id: result[i].matchId, name: teams[0].clubName + ' - ' + teams[1].clubName};

But that doesn't work. That line of code is executed at some point in the future, ie after the for loop has finished and by that time i is -1. To make it work you need a new variable for each iteration of the loop. You could do that eg by wrapping the code in another function like this

for(var i = result.length - 1; i >= 0; i--) {
  (function(i) {
    promises.push(
      DB.Teams.findAll({where: { team_id: [result[i].teamAId,result[i].teamBId]}}).then(function (teams) {
         return { id: result[i].matchId, name: teams[0].clubName + ' - ' + teams[1].clubName};
      }));
  })(i);
}

That way you use a different i variable (stored at a different place in memory) in each iteration. But the best way to do it in this case is to use forEach. The only difference is that the loop will iterate through the array forward and not backward as was the case with your for loop.

result.forEach(function(match) {
  promises.push(
    DB.Teams.findAll({where: { team_id: [match.teamAId,match.teamBId]}}).then(function (teams) {
       return { id: match.matchId, name: teams[0].clubName + ' - ' + teams[1].clubName};
    }));
});

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