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

时间:2017-12-11 20:55:21

标签: javascript mongodb loops promise async-await

我有一个异步地图功能。在这个内部我创建了一组文档,如果它们不存在的话。 对于下一个文档,第一个创建文档的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)
}))

所以我希望这个日志输出具有交替的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)

但我确实得到了

undefined
undefined
undefined
undefined
undefined
undefined
yBZWYJcWBoSzx9qwy
23poGAbg85LCqkcZc
yBJYzedxftLe2Xm4r
PpzjuZ4vfrktNH4ez

它向我显示它不是交替日志输出,而是在两个块中运行,尽管我为await设置了insertOne(使用mongo本机驱动程序)。所以很明显我误解了一些事情:

我认为我正在做的是,在这种情况下map是 - 就像forEach循环一样: 在第一次运行之后,提取现有文档的ID或新创建的文档的ID(如果没有退出)。 在第二次运行中,此ID设置为parentID,因此对于第二个创建的文档,将设置父键。

2 个答案:

答案 0 :(得分:2)

您遇到的问题是,如果您希望评估同步完成,则不希望使用Promise.all。这是因为Promise.all将接受一系列承诺,然后允许这些承诺在他们这样做时得到解决。而是将操作包装在异步函数中,然后使用传统的循环:

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

答案 1 :(得分:2)

我从你的片段中猜测事情实际上是按预期工作的。您看到日志输出的原因是因为它将运行第一个'阶段' (继续下一次等待调用的代码的阶段)在继续之前,为数组的每个项目设置map函数。

请参阅此代码段:



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




因此,虽然您会注意到每个项目的阶段按顺序分别运行,但整个集合的情况并非如此。例如,第1阶段将在它们中的任何一个完成之前为它们中的每一个开始。这只是因为map是同步的,但你传递它的功能并非如此。

如果您绝对想要在继续​​操作之前强制完成项目的所有阶段,则必须单独等待每个阶段,而不是使用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()




但是您注意到完成此操作的时间要长得多,因为每个项目都无法再并行运行。