Async await not work properly inside map function

空扰寡人 提交于 2021-01-29 11:51:20

问题


I have a Dataset Structure like this:

  _id: SomeMongoID,
  value: "a",
  counter: 1
}

So initially my database table is empty. Now I have an array where value is like: const array = ["a", "a", "a"]

What I want is initially the first time I do the search, so it will empty result so at that case in insert the query, now next time it get the entry so simply increment the counter.

For that, I wrote the code:

const testFunction = async(array) => {
  try {
    await Promise.all(
      array.map(async x => {
       const data = await CollectionName.findOne({value: x}).exec();
       // Every time data will return null
       if (data) {
         //In this case only counter will have to increase
         // But this block not run 
       } else {
         //So by this first value will store
         const value = new Value({
          value: x,
          counter: 1
         });
         await value.save()
       }
      })
    )
  } catch (error) {
    console.log(error)
  }
}
const array = ["a", "a", "a"]
testFunction(array);

Problem is it will create the 3 entry not single. map function will not wait, I checked through manual debugging using console.log(). Any help or suggestion is really appreciated.


回答1:


You don't have to use map in here. You can just use a for ... of iteration which will await the results.

const testFunction = async(array) => {
  try {
    for (const x of array) {
      const data = await CollectionName.findOne({value: x}).exec();
      // Every time data will return null
      if (data) {
        //In this case only counter will have to increase
        // But this block not run 
      } else {
        //So by this first value will store
        const value = new Value({
        value: x,
        counter: 1
        });
        await value.save()
      }
    }
  } catch (error) {
    console.log(error)
  }
}
const array = ["a", "a", "a"]
testFunction(array);

Explanation: map does not await even if the pass function is async. Instead it collects the return values of the function you pass into it. async functions always return Promise. Then the Promise.all awaits all of them. But the iteration through the data will not await anything. It will return promise as soon as it evaluates the right side of the first await keyword. But for ... of works because it does not use a callback function. Instead it evaluates each loop and correctly awaits where you want it to do so. The execution is then very close as if all the code inside was synchronous.




回答2:


To save time and load data in parallel, first process the values we have ourselves. We create a data structure that has already counted the same values before even making a single call to the database. Then we only call the database for a unique key in our data structure. This reduces the number of calls in your example from 3 to 1. I my example i added two "b" values to the test data. So the number of calls will be 2 instead of 5.

The database is then queried for the unique keys. If an entry is found, the counter is increased by the number of value occurrences inside the test array. If it is not found a new entry is made with the counter set to the number of found occurences.

const testFunction = async (array) => {
  try {

    // Create a statistics by counting each value fist
    // Creates a structure like { a: 3, b: 2}
    const countsByValues = array.reduce((acc, value) => {
      const newCount = acc[value] ? acc[value] + 1 : 1;
      return {
        ...acc,
        value: newCount
      };
    }, {});

    await Promise.all(
      // Use object entries to get array of [["a", 3], ["b", 2]]
      Object.entries(countsByValues).map(async ([x, count]) => {
       const data = await CollectionName.findOne({value: x}).exec();
       if (data) {
         //In this case only counter will have to increase by the count
         data.counter += count;
         //save data - insert your code saving code here
         await data.save();
       } else {
         //So by this first value will store
         const value = new Value({
          value: x,
          // new values will be the total count
          counter: count
         });
         await value.save()
       }
      })
    )
  } catch (error) {
    console.log(error)
  }
}
const array = ["a", "a", "b", "b", "a"]
testFunction(array);


来源:https://stackoverflow.com/questions/58504988/async-await-not-work-properly-inside-map-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!