1

I wrote a controller and I do not understand why in the method then my array is correct and I need to send it, and for .then () my array is empty. I can not send a res in the middle of the loop.

exports.getRecipientdata = (req, res) => {
  const userId = req.params.recipientId;
  const sendersArray = [];
  Transaction.findAll({
    where: {
      id_recipient: userId,
    },
  }).then(transactions => {
    for (let i = 0; i < transactions.length; i++) {
      User.findOne({
        where: {
          id: transactions[i].id_sender,
        },
        attributes: ['id', 'name', 'surname'],
        include: [
          {
            model: Transaction,
            where: { id_sender: db.Sequelize.col('user.id') },
            attributes: [
              'amount_money',
              'date_time',
              'transfer_title',
              'id_recipient',
              'id_sender',
            ],
          },
        ],
      })
        .then(sender => {
          sendersArray.push(sender);
          console.log(JSON.stringify(sendersArray)); // ok
        })
        .catch(err => {
          console.log(err);
        });
    }
    console.log('sendersArray', sendersArray); // empty?
    res.send(sendersArray);
  });
};
ReactRouter4
  • 165
  • 2
  • 13
  • Well, thats because the empty console message is executed before you actually get async result and update the senders array. Don't forget that you code is asyncronous – Shai Aharoni Jan 20 '19 at 07:31

1 Answers1

1

The for loop is sending out requests which asynchronously populate sendersArray. If you console.log(sendersArray) synchronously after the for loop has run, it won't have been populated yet. Instead of a for loop, use .map and Promise.all to wait for all requests to complete:

exports.getRecipientdata = (req, res) => {
  const userId = req.params.recipientId;
  const sendersArray = [];
  Transaction.findAll({
    where: {
      id_recipient: userId,
    },
  }).then(transactions => {
    return Promise.all(transactions.map(({ id_sender }) => (
      User.findOne({
        where: {
          id: id_sender,
        },
        attributes: ['id', 'name', 'surname'],
        include: [
          {
            model: Transaction,
            where: { id_sender: db.Sequelize.col('user.id') },
            attributes: [
              'amount_money',
              'date_time',
              'transfer_title',
              'id_recipient',
              'id_sender',
            ],
          },
        ],
      })
        .then(sender => {
          sendersArray.push(sender);
        })
        .catch(err => {
          console.log(err);
        })
    )));
  })
  .then(() => {
    res.send(sendersArray);
  });
};

Another possibility, rather than pushing to an outer variable, would be to use the array created by Promise.all, and filter by boolean to remove the falsey values (since the catch's lack of a return value will have resulted in undefineds being present in the result of the Promise.all array):

exports.getRecipientdata = (req, res) => {
  const userId = req.params.recipientId;
  Transaction.findAll({
    where: {
      id_recipient: userId,
    },
  }).then(transactions => {
    return Promise.all(transactions.map(({ id_sender }) => (
      User.findOne({
        where: {
          id: id_sender,
        },
        attributes: ['id', 'name', 'surname'],
        include: [
          {
            model: Transaction,
            where: { id_sender: db.Sequelize.col('user.id') },
            attributes: [
              'amount_money',
              'date_time',
              'transfer_title',
              'id_recipient',
              'id_sender',
            ],
          },
        ],
      })
        .catch(err => {
          console.log(err);
        })
    )));
  })
  .then((sendersArray) => {
    res.send(sendersArray.filter(Boolean));
  });
};
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320