异步等待在地图功能中无法正常工作
Posted
技术标签:
【中文标题】异步等待在地图功能中无法正常工作【英文标题】:Async await not work properly inside map function 【发布时间】:2020-02-18 15:30:47 【问题描述】:我有一个这样的数据集结构:
_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 函数不会等待,我使用 console.log() 通过手动调试检查。非常感谢任何帮助或建议。
【问题讨论】:
为什么要等待?你不是await
ing 从你传递给 map
的函数返回的承诺,直到它们都开始并被包裹在 Promise.all
中
@Quentin 我更新了这个问题。这将创建 3 个条目而不是单个条目。
您可以执行const dataArray = await Promise.all(array.map(x => CollectionName.findOne(value: x))
,然后遍历该dataArray
并执行您需要的任何操作。
【参考方案1】:
您不必在这里使用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
不会等待,即使传递函数是 async
。相反,它收集您传递给它的函数的返回值。 async
函数总是返回 Promise
。然后Promise.all
等待所有人。但是对数据的迭代不会等待任何东西。它将在评估第一个 await
关键字的右侧时立即返回承诺。但是for ... of
有效,因为它不使用回调函数。相反,它评估每个循环并正确等待您希望它这样做的地方。然后执行非常接近,好像里面的所有代码都是同步的。
【讨论】:
在循环中使用 await 是一种反模式,本质上是阻塞行为。应将一组 Promise 传递给 Promise.all 以利用其并行能力。 @DanStarns 你是对的。但是不知道这个人到底想达到什么目的,'sync' for...of 循环是这个地方最安全的解决方案。但是,嘿,我将使用并行性添加另一个答案。【参考方案2】:为了节省时间和并行加载数据,首先处理我们自己拥有的值。我们创建了一个数据结构,它甚至在对数据库进行单次调用之前就已经计算了相同的值。然后我们只为我们的数据结构中的唯一键调用数据库。这将您示例中的调用次数从 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);
【讨论】:
以上是关于异步等待在地图功能中无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章
无法从异步函数获取返回值,等待未按预期工作(Vue API 服务)