繁体   English   中英

异步等待在 map function 内无法正常工作

[英]Async await not work properly inside map function

我有一个这样的数据集结构:

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

所以最初我的数据库表是空的。 现在我有一个数组,其中值如下: const array = ["a", "a", "a"]

我最初想要的是我第一次进行搜索,所以它会清空结果,所以在这种情况下插入查询,现在下次它获取条目时,只需增加计数器。

为此,我编写了代码:

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);

问题是它会创建 3 个条目而不是单个条目。 map function不会等待,我使用console.log()通过手动调试检查。 非常感谢任何帮助或建议。

您不必在这里使用map 您可以只使用for... of迭代来等待结果。

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);

说明: map不会等待,即使通过 function 是async 相反,它收集您传递给它的 function 的返回值。 async函数总是返回Promise 然后Promise.all等待所有这些。 但是对数据的迭代不会等待任何东西。 一旦评估第一个await关键字的右侧,它将返回 promise。 但是for... of作品,因为它不使用回调 function。 相反,它评估每个循环并正确等待您希望它这样做的地方。 然后执行非常接近,好像里面的所有代码都是同步的。

通常, map不会阻止异步代码的迭代,但您可以通过使用此高阶 function 包装异步 function 来实现:

function asyncWrapper(asyncF) {
  let tempP;
  return async (...args) => {
    const lTempP = tempP;
    let r;
    tempP = new Promise(res => r = res);
    if (lTempP) {
      await lTempP
    }
    await asyncF(...args);
    r();
  }
}

演示:

 const testFunction = async(array) => { try { array.map(asyncWrapper(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 console.log(data) } 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"] const CollectionName = []; CollectionName.findOne = (obj) => ({ exec() { return new Promise(r => { setTimeout( () => r( CollectionName.find( o => Object.keys(obj).every(k => obj[k] === o[k]) ) ) ) }) } }) class Value { constructor({value, counter}) { this.value = value; this.counter = counter; } save() { setTimeout( () => CollectionName.push({ value: this.value }) ) } } testFunction(array); function asyncWrapper(asyncF) { let tempP; return async (...args) => { const lTempP = tempP; let r; tempP = new Promise(res => r = res); if (lTempP) { await lTempP } await asyncF(...args); r(); } }

为了节省时间并并行加载数据,首先处理我们自己拥有的值。 我们创建了一个数据结构,它甚至在对数据库进行单次调用之前就已经计算了相同的值。 然后我们只为我们的数据结构中的唯一键调用数据库。 这将您示例中的调用次数从 3 减少到 1。在我的示例中,我向测试数据添加了两个"b"值。 所以调用次数将是 2 而不是 5。

然后在数据库中查询唯一键。 如果找到条目,则计数器会增加测试数组中value出现的次数。 如果未找到,则创建一个新条目,并将计数器设置为找到的出现次数。

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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM