1

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.

2
  • sequelize.js doesn't have the DB.Matches.findAll({attributes: ['matchId', 'teamAId', 'teamBId']}) method. Please read the docs here. Commented May 13, 2015 at 14:45
  • DB.Matches and DB.Teams is a required() schema like: module.exports = function (sequelize, Sequelize) { var module = {}; module = sequelize.define('tbl_team', { blablabla }); return module; }; Commented May 13, 2015 at 14:52

1 Answer 1

5

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, i.e. 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 e.g. 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};
    }));
});
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! That is the point I was missing :) What do you mean by sending to the data channel instead of socket?
Forget what I said about the data channel. I'm not familiar with socket.io.
One more question, if I want to set the id with a walue from the outer query (result[i].matchId) than how I can do that?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.