简体   繁体   English

JS:Async / await似乎不等待循环中的结果

[英]JS: Async/await doesn't seem to wait for result in a loop

I've got an async map function. 我有一个异步映射功能。 Within this I'm creating a set of documents, if they are not existing. 如果没有文档,我将在其中创建一组文档。 The ID of the first created document should be set as parent for the next document. 应该将第一个创建的文档的ID设置为下一个文档的parent文档。

await Promise.all(data.map(async (o, index) => {
  const key = o.name
  const value = o.value
  const query = { [key]: value }
  const parentID = refID

  console.log(parentID);

  if (parentID) query.parent = parentID

  // Check if document is existing
  let ref = await References.findOne(query)
  refID = ref ? ref._id : undefined

  // If not existing, create it
  if (!refID) {
    const refInserted = await References.insertOne(query)
    refID = refInserted ? refInserted.insertedId : undefined
  }

  console.log(refID)
}))

So I would expect this log output with alternating refID and parentID: 因此,我希望此日志输出具有交替的refID和parentID:

undefined (parentID first run)
yBZWYJcWBoSzx9qwy (refID first run)
yBZWYJcWBoSzx9qwy (parentID second run)
23poGAbg85LCqkcZc (refID second run)
23poGAbg85LCqkcZc (parentID third run)
yBJYzedxftLe2Xm4r (refID third run)
yBJYzedxftLe2Xm4r (parentID fourth run)
PpzjuZ4vfrktNH4ez (refID fourth run)

But I do get 但是我得到了

undefined
undefined
undefined
undefined
undefined
undefined
yBZWYJcWBoSzx9qwy
23poGAbg85LCqkcZc
yBJYzedxftLe2Xm4r
PpzjuZ4vfrktNH4ez

which shows me that it is not alternating the log output, but running in two blocks although I've set await for the insertOne (using mongo native driver). 这表明它不是交替输出日志,而是运行在两个块中,尽管我已经为insertOne设置了await (使用mongo本机驱动程序)。 So obviously I'm misunderstanding something: 所以很明显我误会了一些东西:

What I think I'm doing is, that map is - in this case - the same like an forEach loop: After the first run the ID of the existing document or the ID of the new created document (if not exiting) is extracted. 我想我正在做的是,在这种情况下,该map类似于forEach循环:第一次运行后,将提取现有文档的ID或新创建的文档的ID(如果没有退出)。 In the second run, this ID is set as parentID so for the second created document the parent key is set. 在第二次运行中,此ID设置为parentID,因此对于第二个创建的文档,设置了父键。

The problem you have here, is that you do not want to use the Promise.all if you want the evaluation to be done in sync. 这里的问题是,如果您希望同步进行评估,则不想使用Promise.all This is because Promise.all will accept an array of promises, and then allow those promises to be resolved whenever they do. 这是因为Promise.all将接受一系列的诺言,然后允许这些诺言在任何时候都得到解决。 Instead wrap the operation in an async function and then use a conventional loop: 而是将操作包装在异步函数中,然后使用常规循环:

async function processData(data){
    for (var index = data.length - 1; i >= 0; i--) {
        const o = data[index];
        const key = o.name
        const value = o.value
        const query = { [key]: value }
        const parentID = refID

        console.log(parentID);

        if (parentID) query.parent = parentID

        // Check if document is existing
        let ref = await References.findOne(query)
        refID = ref ? ref._id : undefined

        // If not existing, create it
        if (!refID) {
          const refInserted = await References.insertOne(query)
          refID = refInserted ? refInserted.insertedId : undefined
        }

        console.log(refID)
    }
}

Im guessing from you snippet that things are actually working as intended. 我从您的摘要中猜测,事情实际上正在按预期进行。 The reason you are seeing that log output is because it will run the first 'phase' (a phase being the code up to the next await call) of your map function for each item of the array before moving on. 之所以会看到日志输出,是因为它将在继续之前为数组的每个项目运行映射函数的第一个“阶段”(阶段是直到下一个await调用的代码)。

See this snippet: 请参阅以下代码段:

 var arr = [1,2,3,4,5] function timer(dur) { return new Promise(res => { setTimeout(res, dur); }); } async function doStuff() { console.log("Beginning block...") await Promise.all(arr.map(async (x) => { console.log("Starting phase 1 for item:", x); await timer(500); console.log("Phase 1 complete, starting phase 2 for item:", x); await timer(500); console.log("Phase 2 complete for item:", x); })); console.log("Block ended...") } doStuff() 

So while you'll note that the phases for each item individually run sequentially, the same is not true for the entire collection. 因此,尽管您会注意到每个项目的阶段都是依次运行的,但对于整个集合而言却并非如此。 Eg, phase 1 will begin for each of them before it completes for any of them. 例如,阶段1将针对每个阶段开始,然后针对每个阶段完成。 That's just a consequence of the fact that map is synchronous but the function you're passing it is not. 这仅仅是map是同步的,而传递的函数却不是同步的结果。

If you absolutely want to enforce that all phases complete for an item before moving on, you'll have to await each one individually, not using Promise.all : 如果您绝对要强制一项完成所有阶段,然后再继续进行,则必须分别等待每个阶段,而不要使用Promise.all

 var arr = [1,2,3,4,5] function timer(dur) { return new Promise(res => { setTimeout(res, dur); }); } async function doStuff() { console.log("Beginning block...") for (const x of arr) { console.log("Starting phase 1 for item:", x); await timer(500); console.log("Phase 1 complete, starting phase 2 for item:", x); await timer(500); console.log("Phase 2 complete for item:", x); }; console.log("Block ended...") } doStuff() 

But you'll note that the time to complete this is significantly longer, since each item can no longer be run in parallel. 但是您会注意到完成此操作的时间明显更长,因为每个项目都无法再并行运行。

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

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