0

I am trying to query, that would get all of the earning and withdrawal amount and sum, but somehow after the forEach loop is done and exits, all of the updated values I placed in a variable return to their original values.

  var withdrawbalance = 0;
  var totalearning = 0;

  userplans.forEach((up) => {
    DepositEarning.findOne({deposit: up.deposit._id})
      .then(depositearning => {
        withdrawbalance += parseInt(depositearning.WithdrawableBalance, 10);
      });
    Earning.findOne({deposit: up.deposit._id})
      .then(earnings => {
        totalearning += parseInt(earnings.Earning, 10);
    });
  })

  console.log(withdrawbalance);
  console.log(totalearning);
MMizani
  • 121
  • 1
  • 9

5 Answers5

4

You are running asynchronous code inside the forEach and forEach will not wait until the asynchronous code finish, to do so, you must wrap your async code with a waiting primitive, like this

await Promise.all(userplans.map(async (up) => {
    await DepositEarning.findOne({deposit: up.deposit._id})
      .then(depositearning => {
        withdrawbalance += parseInt(depositearning.WithdrawableBalance, 10);
      });
    await Earning.findOne({deposit: up.deposit._id})
      .then(earnings => {
        totalearning += parseInt(earnings.Earning, 10);
    });
  }))
Rami Jarrar
  • 4,523
  • 7
  • 36
  • 52
0

I think the reason could be that DepositEarning.findOne() and Earning.findOne() are both async functions. This means that they don't return immediately with a value, hence your variables are still the same after the loop.

ize8
  • 95
  • 7
0

Here is a functional approach to it that removes the mutation side-effect inside the loop. You need an engine that supports Object Rest spread for this:

async function calculate(userplans) {
    const add = (acc, n) => acc + n;
    const flatten = (acc, e) => ({
        depositEarnings: [...acc.depositEarnings, e.depositEarnings], 
        earnings: [...acc.earnings, e.earnings]
    });

    const {depositEarnings, earnings} = (await Promise.all(userplans
          .map(async (up) => ({
              depositEarnings: await DepositEarning.findOne({deposit: up.deposit._id}),
              earnings: await Earning.findOne({deposit: up.deposit._id})
}))))
          .reduce(flatten, {depositEarnings: [], earnings: []});

    const withdrawalBalance = depositEarnings
          .map(d => parseInt(d.WithdrawableBalance, 10))
          .reduce(add, 0);
    const totalEarning = earnings
          .map(e => parseInt(e.Earning, 10))
          .reduce(add, 0);

    console.log(withdrawalBalance);
    console.log(totalEarning);
}
Josh Wulf
  • 4,727
  • 2
  • 20
  • 34
0

Just to add my two cents,

As other answers have stated, your foreach loop is using asynchronous code. It is making use of some API which is not a part of Js.

The call is pushed to call stack and then it is removed from the call stack. Because the work will be handled by the API now. the call to

console.log(withdrawbalance); console.log(totalearning);

is added to the call stack and what is the value right now? 0 0.

The API, when it is done with the work, will add the function in the task queue and from that, event loop will check if the call stack is empty. Since it is empty right now, will add the function in the call stack.

And it is then run. And then you will be having the real values of withdrawbalance and totalearning values.

Shad
  • 1,185
  • 1
  • 12
  • 27
-1

forEach is asynchronous. So the console.log runs before your mutation loop. You can make it synchronous by using map, but even then - you have asynchronous calls inside it. You will need to flatten it further - probably by doing Promise.all on a map, then reducing the output of that.

Josh Wulf
  • 4,727
  • 2
  • 20
  • 34