【问题标题】:How to deal with race conditions in nodejs如何处理nodejs中的竞争条件
【发布时间】:2021-02-13 00:53:03
【问题描述】:

我正在尝试开发一个 MERN 全栈应用程序,其中前端向我的 nodejs 后端发送对“/createDeckOfCards”的 api 调用。目标是单击一个按钮来创建一副新的纸牌,然后返回创建的纸牌列表。 参数 numOfCards 也与此调用一起发送。

所以在我的 nodeJS 后端,我有“/createDeckOfCards”端点,我使用 .map() 迭代地创建每张卡片,然后像这样保存到 mongoDB:

const allCardsArray = [...Array(req.body.numOfCards).keys()]

allCardsArray.map(async (i)=>{
  const eachCard = new eachCardModel({
    eachCardTitle: String(i)
  })
  
  eachCard.save((err, doc) => {
    if (err) return res.status(400).json({ errMsg: "Something went wrong" });
    else{
      CardDeckModel.findOneAndUpdate(
        {_id: req.cardDeckCreated._id},
        {$push:{allCards: doc}},
        function(error, success){
          if (error){
            console.log(error)
            return res.status(400).json({ errMsg: "Something went wrong" });
          } else {
            console.log("success")
          }
        }
      )
    }
  });
})

console.log("COMPLETED") //DOES NOT EXECUTE LAST!

//THIS RETURNS BEFORE THE .map() is done
res.status(200).json({ 
  createdCardDeckID: req.cardDeckCreated._id 
})
})

之后,我有第二个端点“/returnAllCardsInDeck”,我在其中传入 cardDeck 的 ID,如下所示:

CardDeckModel.findOne({_id: req.body.createdCardDeckID}).populate({path: 'allCards', options: { sort: "eachCardTitle" } }).exec((err, cardDeck) => {
    if (err) return res.status(400).json({ errMsg: "Something went wrong" });
    else {
      res.status(200).json({
        CardDeck: cardDeck
      })
    }
  })

问题是,CardDeck 在 allCardsArray.map() 完成之前返回。这将是一个问题,因为我希望用户在创建卡片组后查看卡片组中的所有卡片。但是因为“/returnAllCardsInDeck”在“/createDeckOfCards”之前执行,所以它返回的是一个未定义的对象。

另外,我这样做对吗?特别是关于第一部分(“/createDeckOfCards”)。

【问题讨论】:

  • 你可以使用 async/await 吗?或者你只想使用回调??
  • 可能会发回409 表明牌组还没有准备好?您是希望在构建甲板之前保留第二个请求,还是要关闭第二个请求并让他们再提出一个请求?
  • @MohammadYaserAhmadi 感谢您的回复!但是你的意思是制作函数然后像下面的答案一样添加等待?因为我试过了,但没有用:/
  • @zero298 感谢您的建议!我不完全知道哪种方法是正确的,但是第二个请求只能在构建卡片组后运行,因为它试图返回卡片组中的所有卡片(迭代地尝试在我的地图函数中创建)。

标签: javascript node.js mongoose race-condition mern


【解决方案1】:

试试这个,你不能用 map 做这样的异步调用。有一些模式可以解决这个问题。 Promise.all 就是其中之一。

const allCardsArray = [...Array(req.body.numOfCards).keys()]

await Promise.all(
    allCardsArray.map((i)=>{
        const eachCard = new eachCardModel({
          eachCardTitle: String(i)
        })

        return eachCard.save()
            .then(
                () => 
                    CardDeckModel.findOneAndUpdate({_id: req.cardDeckCreated._id}, {$push:{allCards: doc}})
                    .then(() => console.log("success"))
                    .catch((error) => console.log(error) || res.status(400).json({ errMsg: "Something went wrong" });)
        ).catch(() => res.status(400).json({ errMsg: "Something went wrong" }))
      })
)

    console.log("COMPLETED") //DOES NOT EXECUTE LAST!

    //THIS RETURNS BEFORE THE .map() is done
    res.status(200).json({
        createdCardDeckID: req.cardDeckCreated._id
    })
})

【讨论】:

  • 感谢您的帮助!但我只是尝试过,但不知何故它不起作用......即使在我按照你的建议做之后它也没有等待......
  • 尝试返回 eachCard.save
  • 哦,对不起,您将回调传递给 save 和 findOneAndUpdate,因此它们不会返回 Promise。我会为你重构这个给我一秒钟
【解决方案2】:

您可以将for ofasync/await 一起使用,而不是像这样使用map

const allCardsArray = [...Array(req.body.numOfCards).keys()];

for (let i of allCardsArray) {
  const eachCard = new eachCardModel({
    eachCardTitle: String(i),
  });
  try {
    let doc = await eachCard.save();
    await CardDeckModel.findOneAndUpdate(
      { _id: req.cardDeckCreated._id },
      { $push: { allCards: doc } }
    );
  } catch (error) {
    return res.status(400).json({ errMsg: "Something went wrong" });
  }
}
console.log("success");
res.status(200).json({
  createdCardDeckID: req.cardDeckCreated._id,
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 2011-04-01
    • 2014-02-23
    相关资源
    最近更新 更多