【问题标题】:How to make Mongoose update work with await?如何使猫鼬更新与等待一起工作?
【发布时间】:2019-12-12 16:40:59
【问题描述】:

我正在创建一个 NodeJS 后端,其中一个进程从源读取数据,检查与当前数据相比的更改,对 MongoDB 进行这些更新并报告所做的更改。一切正常,除了我无法报告更改,因为我无法等待 Mongoose 更新操作。

这个函数返回的数组然后由 Koa 服务器显示。它显示一个空数组,并且在服务器日志中,服务器返回空响应后会显示正确的值。

我已经深入研究了 Mongoose 文档和 Stack Overflow 问题——关于这个主题的很多问题——但没有成功。提供的解决方案似乎都没有帮助。我已将问题隔离到这一部分:如果我删除 Mongoose 部分,一切都会按预期工作。

const parseJSON = async xmlData => {
    const changes = []

    const games = await Game.find({})
    const gameObjects = games.map(game => {
        return new GameObject(game.name, game.id, game)
    })

    let jsonObj = require("../sample.json")
    Object.keys(jsonObj.items.item).forEach(async item => {
        const game = jsonObj.items.item[item]
        const gameID = game["@_objectid"]
        const rating = game.stats.rating["@_value"]
        if (rating === "N/A") return
        const gameObject = await gameObjects.find(
            game => game.bgg === parseInt(gameID)
        )
        if (gameObject && gameObject.rating !== parseInt(rating)) {
            try {
                const updated = await Game.findOneAndUpdate(
                    { _id: gameObject.id },
                    { rating: rating },
                    { new: true }
                ).exec()
                changes.push(
                    `${updated.name}: ${gameObject.rating} -> ${updated.rating}`
                )
            } catch (error) {
                console.log(error)
            }
        }
    })

    return changes
}

一切正常 - 找到更改并更新数据库,但报告的更改返回得太晚,因为执行不等待 Mongoose。

我也试过这个而不是findOneAndUpdate():

const updated = await Game.findOne()
    .where("_id")
    .in([gameObject.id])
    .exec()
updated.rating = rating
await updated.save()

这里的结果相同:其他一切正常,但异步不起作用。

【问题讨论】:

  • 猫鼬版本是什么?
  • Promise 不会被 forEach 等待,因为它是同步的。把你的承诺放在一个数组中,然后在上面做 Promise.all
  • @PuneetSharma Mongoose 5.5.8。看起来 forEach() 确实是这里的问题。
  • 是的。换成await Promise.all(jsonObj.items.item).map(async(item => {}))
  • @krbalaji "Promise.all(...).map 不是函数"

标签: node.js mongodb mongoose async-await


【解决方案1】:

正如@Puneet Sharma 提到的,您必须map 而不是forEach 才能获得一系列承诺,然后在承诺上使用await(为方便起见,使用Promise.all),然后返回changes然后将被填充:

const parseJSON = async xmlData => {
    const changes = []

    const games = await Game.find({})
    const gameObjects = games.map(game => {
        return new GameObject(game.name, game.id, game)
    })

    const jsonObj = require("../sample.json")
    const promises = Object.keys(jsonObj.items.item).map(async item => {
        const game = jsonObj.items.item[item]
        const gameID = game["@_objectid"]
        const rating = game.stats.rating["@_value"]
        if (rating === "N/A") return
        const gameObject = await gameObjects.find(
            game => game.bgg === parseInt(gameID)
        )
        if (gameObject && gameObject.rating !== parseInt(rating)) {
            try {
                const updated = await Game.findOneAndUpdate(
                    { _id: gameObject.id },
                    { rating: rating },
                    { new: true }
                ).exec()
                changes.push(
                    `${updated.name}: ${gameObject.rating} -> ${updated.rating}`
                )
            } catch (error) {
                console.log(error)
            }
        }
    })

    await Promise.all(promises)
    return changes
}

(差异,为方便起见:

9,10c9,10
<     let jsonObj = require("../sample.json")
<     Object.keys(jsonObj.items.item).forEach(async item => {
---
>     const jsonObj = require("../sample.json")
>     const promises = Object.keys(jsonObj.items.item).map(async item => {
33a34
>     await Promise.all(promises)

)


编辑:进一步的重构将是使用该承诺数组作为更改描述本身。基本上changePromisesPromises 的数组,它们解析为字符串或null(如果没有更改),因此带有标识函数的.filter 将过滤掉虚假值。

此方法还有一个优点,即changes 将与键被迭代的顺序相同;使用原始代码,无法保证顺序。这对您的用例可能很重要,也可能无关紧要。

我还翻转了 map 函数中的 if/else 以减少嵌套;这真的是品味问题。

附言。当您拥有大量游戏时,await Game.find({}) 将成为问题。

const parseJSON = async xmlData => {
  const games = await Game.find({});
  const gameObjects = games.map(game => new GameObject(game.name, game.id, game));

  const jsonGames = require("../sample.json").items.item;

  const changePromises = Object.keys(jsonGames).map(async item => {
    const game = jsonGames[item];
    const gameID = game["@_objectid"];
    const rating = game.stats.rating["@_value"];
    if (rating === "N/A") {
      // Rating from data is N/A, we don't need to update anything.
      return null;
    }
    const gameObject = await gameObjects.find(game => game.bgg === parseInt(gameID));
    if (!(gameObject && gameObject.rating !== parseInt(rating))) {
      // Game not found or its rating is already correct; no change.
      return null;
    }
    try {
      const updated = await Game.findOneAndUpdate(
        { _id: gameObject.id },
        { rating: rating },
        { new: true },
      ).exec();
      return `${updated.name}: ${gameObject.rating} -> ${updated.rating}`;
    } catch (error) {
      console.log(error);
    }
  });

  // Await for the change promises to resolve, then filter out the `null`s.
  return (await Promise.all(changePromises)).filter(c => c);
};

【讨论】:

  • 优秀。看起来我还有很多东西要学习 async/await 和 promises...
猜你喜欢
  • 2018-07-24
  • 2018-11-29
  • 2022-12-18
  • 2018-05-14
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
相关资源
最近更新 更多