使用嵌套承诺和等待的最佳方法是什么

时间:2021-03-11 23:41:44

标签: javascript node.js mongoose

我想获取当前用户的所有帖子和他朋友的所有帖子。代码正在运行,但我对同时使用 async-await 和 Promise.all 感到困惑。当我一起使用它们时我应该如何处理错误以及使用嵌套异步函数的最佳方法是什么?提前致谢。

exports.delete = function (req, res, next) {
res.send("hi") 
[enter image description here][1]//this would be a render but I am just testing to see if it will reload or redirect to a new page

verify 中间件由 JWT 验证器提供。我的模型是这样的:

router.get("/timeline", verify, async(req, res, next) => {
  let postArray = [];
  try {
    const posts = await Post.find({
      userId: req.user.id
    });
    postArray.push(...posts);
    const currentUser = await User.findById(req.user.id);
    const promises = currentUser.friends.map(async(friendId) => {
      const posts = await Post.find({
        userId: friendId
      });
      posts.map((p) => postArray.push(p));
    });
    await Promise.all(promises).then(() => res.send(postArray));
  } catch (err) {
    next(err);
  }
});
const UserSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      min: 3,
      max: 20,
      unique: true,
    },
    email: {
      type: String,
      required: true,
      max: 50,
      unique: true,
    },
    password: {
      type: String,
      required: true,
      min: 6,
      max: 1024,
    },
    profilePicture: {
      type: String,
      default: "",
    },
    coverPicture: {
      type: String,
      default: "",
    },
    isAdmin: {
      type: Boolean,
      default: false,
    },
    friends: {
      type: Array,
      default: [],
    },
  },
  { timestamps: true }
);

2 个答案:

答案 0 :(得分:0)

这是我的简化版本:

router.get("/timeline", verify, async (req, res, next) => {
    try {
        const postArray = await Post.find({ userId: req.user.id });
        const currentUser = await User.findById(req.user.id);
        await Promise.all(currentUser.friends.map(friendId => {
            return Post.find({ userId: friendId }).then(fPosts => postArray.push(...fPosts));
        }));
        res.send(postArray);
    } catch (err) {
        next(err);
    }
});

变化:

  1. 只需使用第一位数据初始化 postArray,而不是将其声明为空数组,然后从另一个数组推入它。
  2. 不是每次都迭代 posts,只需一次操作将它们全部推入 postArray
  3. 我不喜欢在 async 中使用带有 await.map() 回调,因为这对普通读者来说意味着 .map() 循环实际上会暂停await(它不会)。因此,我只是使用 .then() 将朋友的帖子收集到 postArray 中,然后从 Post.find() 返回承诺。
<块引用>

我对同时使用 async-await 和 Promise.all 感到困惑

当您有多个承诺并且想知道它们何时全部完成时,您可以使用 Promise.all()。这可以与 async/await 一起使用,因为它们也对 promise 进行操作。在我的示例中,我在 return Post.find() 循环中执行 .map(),这意味着 .map() 的返回将是一个 Promise 数组,非常适合与 Promise.all() 一起使用以了解它们何时都搞定了。

<块引用>

一起使用时应该如何处理错误

如果您使用 await Promise.all(),那么它的错误处理就像您所有其他 await 语句一样。您周围的 try/catch 会发现错误。

<块引用>

使用嵌套异步函数的最佳方法是什么

这个问题真的没有唯一的答案。你有一种有效的方法。我提供了一个对我来说似乎更简单的轻微变化。旁观者眼中的简单。


仅供参考,有些人不喜欢在同一代码块中看到 .then()await - 期望您使用一种或另一种习语。我不喜欢那种非黑即白的规则,但更喜欢使用对我来说似乎更简单的东西,偶尔会看到我在特定情况下混合使用它们。

如果您愿意,这是上面代码的修改版本,没有 .then()

router.get("/timeline", verify, async (req, res, next) => {
    try {
        const postArray = await Post.find({ userId: req.user.id });
        const currentUser = await User.findById(req.user.id);
        await Promise.all(currentUser.friends.map(async friendId => {
            let fPosts = await Post.find({ userId: friendId });
            postArray.push(...fPosts);
        }));
        res.send(postArray);
    } catch (err) {
        next(err);
    }
});

答案 1 :(得分:0)

jfriend00 的回答很棒。只需添加它,如果您想在 api 无法加载朋友的帖子时显示自定义错误,您可以通过执行以下操作来实现。

router.get("/timeline", verify, async (req, res, next) => {
    try {
        const postArray = await Post.find({ userId: req.user.id });
        const currentUser = await User.findById(req.user.id);
        await Promise.all(currentUser.friends.map(async friendId => {
            let fPosts = await Post.find({ userId: friendId });
            postArray.push(...fPosts);
        })).catch((e) => {
             throw new Error("Failed to load friend's post")    // notice a separate catch 
        })
        res.send(postArray);
    } catch (err) {
        next(err);
    }
});