如何等待异步操作循环?

How to Await a Loop of Async Operations?

我有一个补丁端点,它接受要更新的字段主体、遍历每个字段并更新正确的值。一旦完成所有这些事情,我想做一些事情发送响应,但我不知道如何等待操作循环。如果它是一个单独的操作,我可以只添加 .then(),但在这种情况下这不起作用。什么是优雅的解决方案?

代码:

const updateUser = (req, res) => {
    const db = mongoConnection.getDb();
    for (key in req.body) {
        db.collection('users').updateOne(
            {username: req.query.username},
            {$set: {[key]: req.body[key]}}
        )
    }
    // send response once for loop is done
}

我认为一个简单的答案是将 for 循环隐藏在异步函数中,然后在端点中使用它。

const updateUserField = async () =>
  db.collection("users").updateOne(
    {
      username: req.query.username,
    },
    {
      $set: { [key]: req.body[key] },
    }
  );


const updateUserEndpoint = (req, res) => {
   for (let field of req.body) {
      await updateUserField(field);
   }

   res.status(200).send('updated');
}

然后,为了进行测试,您可以使用注入函数本身的函数包装端点:

const updateUserEndpoint = (userUpdateFunc) => userUpdateFunc(req, res);

这种模式称为依赖注入 - 在为端点编写单元测试时,您只需将 userUpdateFunc 替换为您想要使用的任何模拟函数。这消除了在测试中对函数进行 monkeypatch 的需要。

您可以标记外部函数 asyncawait 循环内的每个数据库更新。然后你就会知道在循环完成后,所有的数据库更新都完成了。

更好的方法是 运行 并行更新,因为它们不相互依赖。您可以使用 Promise.allSettled() 它接受一组承诺并在最后一个完成时解决。

const updateUser = async (req, res) => {
  const db = mongoConnection.getDb();
  
  const dbUpdates = Object.entries(req.body).map((key, value) => {
    return db.collection('users').updateOne(
      { username: req.query.username },
      { $set: { [key]: value }}
    );
  });

  await Promise.allSettled(dbUpdates);

  // Send response, at this point all DB updates are done
};