0

I am trying to use async and await on my react js app. But it's not working correctly.

const handleFinish = async () => {
    const startDateString = startDate.format('YYYY-MM-DDThh:mm');
    const endDateString = endDate.format('YYYY-MM-DDThh:mm');
    const createdString = moment().format('YYYY-MM-DDThh:mm');
    const requestData = [];
    const previousData = [];
    selectedCustomerData.forEach(customer => {
      selectedProductData.forEach(product => {
        previousData.push({
          customer: customer,
          product: product
        });
      });
    });
    await previousData.forEach(async element => {
      const tempStartDate = moment().add(element.customer.leadtime, 'days');
      const apiUrl = '/productprice/?customerid=' + element.customer.customerid + 
                      '&productkey=' + element.product.productkey + 
                      '&isrulendNull=true';
      await api.get(apiUrl).then(response => {
        let newPrice = 0;
        if (priceMethod === 'fixed') {
          newPrice = price;
        } else if (priceMethod === 'specific') {
          newPrice = response.data[0].productpriceusd + price;
        } else if (priceMethod === 'relative') {
          newPrice = response.data[0].productpriceusd % 100.0 * (100.0 + price);
        }
        requestData.push({
          productkey: element.product.productkey,
          productcode: element.product.productcode,
          customerid: element.customer.customerid,
          customerchain: element.customer.customerchain,
          productpriceusd: newPrice,
          rulestart: (tempStartDate > startDate) ? tempStartDate.format('YYYY-MM-DDThh:mm') : startDateString,
          ruleend: endDateString,
          creatorupn: user.data.displayName,
          created: createdString,
          customername: element.customer.customername,
          productdescription: element.product.productdescription,
        });
      });
    });
    console.log(requestData);
    setPricesList(requestData);
  };

I expected the requestData array after foreach. But that console.log does not occur after foreach, it occurs before foreach. That means async and await is not working. What's wrong in my code?

Blake
  • 259
  • 5
  • 23
  • have a look here: https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – mikemols Dec 17 '20 at 10:36
  • 1
    Generally speaking, you dont want to have both await and then() in your code; either stick to one or the other to avoid problems. In this case as Oleksandr pointed out, you're using .foreach which can't be used with async/await – Juanjo Martinez Dec 17 '20 at 10:40

5 Answers5

5

I made this mistake quite often myself...

await previousData.forEach(async element => {

this kind of stuff does not synchronize the forEach loop. They are still triggered at the same time, and the await at the beginning of the line does not wait for them. I would use Promise.all or a classic loop like for or while.

Paflow
  • 2,030
  • 3
  • 30
  • 50
2

Array.forEach() doesn't wait for all elements to be processed in the given async function.

You can use plain for loop like:

const handleFinish = async () => {
    const startDateString = startDate.format('YYYY-MM-DDThh:mm');
    const endDateString = endDate.format('YYYY-MM-DDThh:mm');
    const createdString = moment().format('YYYY-MM-DDThh:mm');
    const requestData = [];
    const previousData = [];
    selectedCustomerData.forEach(customer => {
      selectedProductData.forEach(product => {
        previousData.push({
          customer: customer,
          product: product
        });
      });
    });

    for (const element of previousData) {
      const tempStartDate = moment().add(element.customer.leadtime, 'days');
      const apiUrl = '/productprice/?customerid=' + element.customer.customerid + 
                      '&productkey=' + element.product.productkey + 
                      '&isrulendNull=true';
      await api.get(apiUrl).then(response => {
        let newPrice = 0;
        if (priceMethod === 'fixed') {
          newPrice = price;
        } else if (priceMethod === 'specific') {
          newPrice = response.data[0].productpriceusd + price;
        } else if (priceMethod === 'relative') {
          newPrice = response.data[0].productpriceusd % 100.0 * (100.0 + price);
        }
        requestData.push({
          productkey: element.product.productkey,
          productcode: element.product.productcode,
          customerid: element.customer.customerid,
          customerchain: element.customer.customerchain,
          productpriceusd: newPrice,
          rulestart: (tempStartDate > startDate) ? tempStartDate.format('YYYY-MM-DDThh:mm') : startDateString,
          ruleend: endDateString,
          creatorupn: user.data.displayName,
          created: createdString,
          customername: element.customer.customername,
          productdescription: element.product.productdescription,
        });
      });
    }

    console.log(requestData);
    setPricesList(requestData);
  };
Potados
  • 177
  • 1
  • 9
0

async/await will not work in forEach loop, you may use for..of loop. here is detailed discussion

Oleksandr Hrin
  • 757
  • 1
  • 10
  • 12
0

async/await is not working correctly on forEach.

Please refer below url.

https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404

  • Links to external resources are encouraged, but please [add context around the link](https://meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline. [Answers that are little more than a link may be deleted](https://stackoverflow.com/help/deleted-answers) – Sabito stands with Ukraine Dec 17 '20 at 12:37
  • Note to reviewers: I have not flagged this one. – Sabito stands with Ukraine Dec 17 '20 at 12:37
0

With ES2018, you are able to greatly simplify all of the above answers to:

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}