【问题标题】:Mongoose prevent next then() to be executedMongoose 阻止下一个 then() 被执行
【发布时间】:2019-09-23 06:32:44
【问题描述】:

使用 Mongoose,我将任务保存到 task 集合中,如果进展顺利,应该使用任务数据更新板。但是,如果上一个(保存任务)出错,我不知道如何防止执行最后一个then()(保存到板)。

我一直在尝试使用Promise.reject(),但我不确定如何返回Promise.reject() 和错误的响应状态。

// POST add a new task
taskRouter.post('/:boardId', (req, res) => {
  let task = new Task(req.body)
  let boardId = req.params.boardId
  let newTaskNumber
  let newTaskId

  // get lastTaskNumber from the board
  Board.findById(boardId)
    .select('lastTaskNumber')
    .exec()
    .then(board => {
      newTaskNumber = board.lastTaskNumber + increaseTaskNumberBy
      newTaskId = `${boardId}-${newTaskNumber}`
      task._id = newTaskId
      return
    })
    // save the task in the task collection
    .then(() => {
      return task.save(
        (err, task) => {
          if (err) {
            return res.status(400).json({
              message: 'Error saving a task',
              error: err
            })
          } else {
            res.status(200).json(task)
          }
        },
        { _id: false }
      )
    })
    // *********
    //if the above fails, don't execute the next then() 
    // *********
    .catch(() => console.log('error saving a task'))
    // update lastTaskNumber in the board
    // add task to the 1st column of the board
    .then(() => {
      return Board.findByIdAndUpdate(boardId, {
        lastTaskNumber: newTaskNumber,
        $push: {
          'columns.0.tasks': {
            taskId: newTaskId,
            title: task.title,
            priority: task.priority
          }
        }
      })
    })
})

【问题讨论】:

  • 这不是进行多文档操作的正确方法。如果执行这些操作相互依赖,则需要使用 mongodb 事务。查看相同的文档。 docs.mongodb.com/master/core/transactions/…
  • 写得有点混乱,而且可能不正确,因为有一个外部task 和一个内部task。您想要task.titletask.priority 的位置尚不清楚您希望这些是外部属性还是内部task 的属性。正如所写,它是外部的。
  • @cEeNiKc,我正在努力解决这里的交易需求。你能帮我解释一下吗?
  • 请参阅 mongodb 提供了对单个文档进行操作的原子性,即,如果您正在更改单个文档的 4 个字段,您可以确保所有这些更改都将被保存,或者它们都不会被保存。但是在您的情况下,您想要创建一个任务,然后如果它创建时没有任何错误,您想要更新不同 Boards 集合中的文档。对于这个用例,您需要使用 mongodb 事务,这将确保所有更改都被保存或都不保存。如果你可以请使用新的异步等待语法和 try catch 块,它会让你的代码更好。
  • 这是一篇非常好的关于在猫鼬中使用事务的博文。看看这个,它会让事情更清楚。 thecodebarbarian.com/…

标签: node.js mongoose promise


【解决方案1】:

这是 mongoose 中的工作示例,其中包含 cmets 中建议的 async/awaittransactions

按顺序调用调用,在前一个调用完成后开始下一个调用。通过使用事务,所有异步函数只有一个catch。如果其中一个调用失败,所有以前的更改都将从数据库中恢复,并调用 catch

taskRouter.post('/:boardId', (req, res) => {
  let task = new Task(req.body)
  let boardId = req.params.boardId
  let newTaskNumber
  let newTaskId

    // tslint:disable-next-line
  ;(async function addTask() {
    const session = await mongoose.startSession()
    session.startTransaction()

    try {
      await Board.findById(boardId, null, { session })
        .select('lastTaskNumber -_id')
        .exec()
        .then(board => {
          newTaskNumber = board.lastTaskNumber + increaseTaskNumberBy
          newTaskId = `${boardId}-${newTaskNumber}`
          task._id = newTaskId
        })

      await Task.create([task], { session }, null, {
        _id: false
      })

      await Board.findByIdAndUpdate(
        boardId,
        {
          lastTaskNumber: newTaskNumber,
          $push: {
            'columns.0.tasks': {
              taskId: newTaskId,
              title: task.title,
              priority: task.priority
            }
          }
        },
        { session }
      )

      await session.commitTransaction()
      session.endSession()
      return res.status(200).json(task)
    } catch (error) {
      await session.abortTransaction()
      session.endSession()
      return res.status(400).json({
        message: 'Error saving a new task',
        error: error
      })
    }
  })()
})

【讨论】:

  • Dandy,事务的引入并不是导致调用按顺序调用的原因。事务的唯一原因是使所有“已处理”的数据库活动被接受或拒绝。您对事务的使用可能是有益的,但它不负责排序。您已经回答了您的标题问题,通过使用 try/catch 和 async/await 语法重写代码(尽管一个 .then() 幸存下来)并采用​​稍微不同的方法来防止执行 next then()。您的原始代码的整理版本没有理由不工作。
  • 是的,你是对的,我在评论中描述了 async/await 错误,我更新了它。我也不知道为什么我最初的想法行不通,但无论哪种方式,交易都是比仅承诺更好的解决方案。
  • 您的原始代码有两处错误。 (1) task.save() 接受一个 nodeback 并且很可能不会返回 Promise;因此,它对 Promise 链没有贡献 - 它的异步性是一个“侧射”(2)你的 .catch() 在那个位置,需要重新抛出错误(或其他一些错误)以便执行以下操作.then() 不被执行。更好的解决方案是将.catch() 移动到链的末尾,或者可能将其保留在原处并在链的末尾添加另一个.catch(),具体取决于您希望报告的错误消息的具体程度.
【解决方案2】:
    Board.findById(boardId)
        .select('lastTaskNumber')
        .exec()
        .then(board => {
             newTaskNumber = board.lastTaskNumber + increaseTaskNumberBy
             newTaskId = `${boardId}-${newTaskNumber}`  
             task._id = newTaskId 
             return
        }) // save the task in the task collection
       .then(() => { 
             return task.save( (err, task) => { 
                   if (err) { 
                        return res.status(400).json({ message: 'Error saving a task', error: err }) 
                   }
                   return Board.findByIdAndUpdate(boardId, 
                   {
                        lastTaskNumber: newTaskNumber, 
                        $push: { 
                             'columns.0.tasks': {
                                  taskId: newTaskId,
                                  title: task.title, 
                                  priority: task.priority 
                              } 
                        } 
                   }) 
             }, 
             { _id: false } ) 
        })
       // *********
       //if the above fails, don't execute the next then()
      // ********* 
      .catch(() => console.log('error saving a task')) 

结构改变了,但应该这样做

【讨论】:

  • 不行,Board.findByIdAndUpdate()现在执行不管task.save()成功与否,完全没有条件。
  • 我开始相信我需要一个标志变量才能使其工作......
猜你喜欢
  • 1970-01-01
  • 2018-03-18
  • 1970-01-01
  • 2020-02-21
  • 2016-09-25
  • 2019-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多