【问题标题】:Node.js / Express / mongoose backend - async operations within array.map()Node.js / Express / mongoose 后端 - array.map() 中的异步操作
【发布时间】:2021-04-28 07:40:35
【问题描述】:

我正在为一家纸质音乐杂志构建我的第一个个人项目,以便为他们的订阅者提供在线阅读。我已经知道我将不得不将大量重复的代码重构为可重用的代码,但我只是希望现在可以正常工作。

这些是我用来构建模型/控制器的集合:

  • 艺术家
  • 专辑
  • 用户(管理员、作者、订阅者)
  • 编年史(专辑短评)
  • 文章(专辑长评论)
  • 采访(大部分时间与文章相关)
  • 问题,参考所有编年史、文章和访谈(ObjectID 数组)

Artist 简单明了,只有 'name'、'country' 和一个空的 'albums' 数组:

exports.createArtist = catchAsync(async (req, res, next) => {
  req.body.createdBy = mongoose.Types.ObjectId(req.user._id)
  req.body.albums = []
  const newArtist = await Artist.create(req.body)

  res.status(201).json({
    status: 'success',
    data: {
      article: newArtist,
    },
  })
})

专辑有点复杂,但是艺术家“姓名”被转换为它的 ObjectId 以供参考,并用新创建的专辑 ObjectId 填充艺术家的“专辑”数组

exports.createAlbum = catchAsync(async (req, res, next) => {
  // Add createdBy automatically in the req.body
  req.body.createdBy = mongoose.Types.ObjectId(req.user._id)

  // Find the album's artist by its name
  const relatedArtist = await Artist.findOne({ name: req.body.artist })

  if (!relatedArtist) {
    return next(new AppError('No artist with that name, please check', 404))
  }
  // replace the name of the artist with its objectID for auto referencing
  req.body.artist = mongoose.Types.ObjectId(relatedArtist._id)

  // save album
  const newAlbum = await Album.create(req.body)

  // add the saved album into the albums' array in the artist collection
  relatedArtist.albums.push(mongoose.Types.ObjectId(newAlbum._id))
  await relatedArtist.save()

  res.status(201).json({
    status: 'success',
    data: {
      album: newAlbum,
    },
  })
})

Chronicle 有点相似,但在此过程中涉及 3 个其他集合:专辑和艺术家数据的收集,以及填充当前问题的“chronicles”数组:

exports.createChronicle = catchAsync(async (req, res, next) => {
  // Add author automatically in the req.body
  req.body.author = mongoose.Types.ObjectId(req.user._id)

  // Find artist & album of the chronicle in the respective collections
  const artist = await Artist.findOne({ name: req.body.artist })
  const album = await Album.findOne({ title: req.body.album })
  const issue = await Issue.findOne({ issueNumber: req.body.belongsToIssue })

  if (!artist) {
    return next(new AppError('No artist with that name, please check.', 404))
  }
  if (!album) {
    return next(new AppError('No album with that name, please check.', 404))
  }
  if (!issue) {
    return next(new AppError('No issue with that number, please check.', 404))
  }

  // replace the name of the artist and album with its objectID for auto referencing
  req.body.artist = mongoose.Types.ObjectId(artist._id)
  req.body.album = mongoose.Types.ObjectId(album._id)

  // Create Chronicle unique slug & add to req.body
  req.body.slug = slugify(`${artist.name} ${album.title} ${album.year}`, {
    lower: true,
  })

  // Save new Chronicle
  const newChronicle = await Chronicle.create(req.body)

  // Push new Chronicle ID into the array of the corresponding Issue
  issue.chronicles.push(mongoose.Types.ObjectId(newChronicle._id))
  await issue.save()

  res.status(201).json({
    status: 'success',
    data: {
      chronicle: newChronicle,
    },
  })
})

我的问题出现在“文章”上: 一篇文章可以是关于 几个 专辑(所以不仅仅是一个 ObjectId,而是一个 ObjectId 的数组!)并且可以由 几个 作家(1 到 3 之间)签名。每次执行时我都必须遍历两个数组:

await Album.find({title: req.body.title})
await User.find({author: req.body.author})

然后通过其 ObjectId 交换 req.body.albums 和 req.body.authors 中的名称,最后将 req.body.albums + authors 从字符串数组转换为 ObjectIds 数组,尤其是在数组是指针(我猜我必须使用解构的重复数组)。 我知道我无法在 map() 循环的 forEach() 中执行异步操作,但还没有弄清楚如何使这项工作。我的研究使我认为我必须使用 Promise.all() 但到目前为止还没有弄清楚如何使用,直到现在我所有的试验和错误都失败了,所以我必须以错误的方式执行此操作或不明白这个过程。

感谢您的帮助和技巧!

【问题讨论】:

  • 类似const albums = await Promise.all(titles.map(title => Album.find({title: title})))

标签: javascript node.js arrays mongoose async-await


【解决方案1】:

谢谢阿纳托利。您的提示让我发现了一些需要优化和重构的东西,但它具有完全功能性的巨大优势:

exports.createArticle = catchAsync(async (req, res, next) => {
  // Add authors automatically in the req.body if not specified by user
  if (!req.body.authors || req.body.authors === []) {
    req.body.authors.push(mongoose.Types.ObjectId(req.user._id))
  }

  // loop through all authors names and swap with respective ObjectIds
  const tempAuthors = []
  await Promise.all(
    req.body.authors.map(async (author, index) => {
      const user = await User.findOne({ name: author })
      if (!user) {
        return next(
          new AppError(
            `No author with that name (position ${index + 1}), please check.`,
            404
          )
        )
      }
      tempAuthors.push(mongoose.Types.ObjectId(user._id))
    })
  )

  // Assign req.body.authors the values of tempAuthors
  req.body.authors = [...tempAuthors]

  // loop through all album titles and swap with respective ObjectIds
  const tempAlbums = []
  await Promise.all(
    req.body.albums.map(async (title, index) => {
      const album = await Album.findOne({ title })
      if (!album) {
        return next(
          new AppError(
            `No album with that title (position ${index + 1}), please check.`,
            404
          )
        )
      }
      tempAlbums.push(mongoose.Types.ObjectId(album._id))
    })
  )

  // Assign req.body.authors the values of tempAuthors
  req.body.albums = [...tempAlbums]

  // Find artist & issue of the article
  const artist = await Artist.findOne({ name: req.body.artist })
  if (!artist) {
    return next(new AppError('No artist with that name, please check.', 404))
  }

  const issue = await Issue.findOne({ issueNumber: req.body.belongsToIssue })
  if (!issue) {
    return next(new AppError('No issue with that number, please check.', 404))
  }

  // replace the name of the artist with its objectID for auto referencing
  req.body.artist = mongoose.Types.ObjectId(artist._id)

  // Create Article unique slug & add to req.body
  req.body.slug = slugify(
    `${issue.issueNumber} ${artist.name} ${req.body.title}`,
    {
      lower: true,
    }
  )

  // Save new Article
  const newArticle = await Article.create(req.body)

  // Push new Article ID into the array of the corresponding Issue
  issue.articles.push(mongoose.Types.ObjectId(newArticle._id))
  await issue.save()

  res.status(201).json({
    status: 'success',
    data: {
      article: newArticle,
    },
  })
})

下一步将把专辑和作者的交换功能外包,以避免重复代码。

【讨论】:

    猜你喜欢
    • 2012-09-27
    • 2016-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-19
    • 2017-02-04
    • 1970-01-01
    • 2016-01-02
    相关资源
    最近更新 更多