在另一个被认为是反模式的承诺中使用承诺?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在另一个被认为是反模式的承诺中使用承诺?相关的知识,希望对你有一定的参考价值。

我有一个代码如下:

app.post("/api/exercise/add", function(req, res, next) {
User
 .findById(req.body.userId)
 .exec()
 .then(user => user)
 .then(function(user) {
   let exercise = new Exercise({
     description: req.body.description,
     duration: req.body.duration,
     date: req.body.date, //BUG: must add validations, date accepts 19984-01-01
     user: user
   })
   .save()
   .then(function(exercise) {
     user.exercises.push(exercise)
     user.
      save().
      then(user => res.json({ status: 201, exercises: user.exercises }))
  })
  .catch(err => next(err))
})
.catch(err => next(err));
});

事实是我在另一个承诺中使用了承诺,在这种情况下,被认为是一种反模式吗?

答案

从某种意义上说,它是不优雅的 - 问题是它创造了不必要的.then嵌套。如果跟随两个Promises的.then.catch处理程序是相同的,你可以return .then内的新Promise将它传递到下一个.then.catch,如下面的代码所示。

要将多个变量/ Promises传递给下一个.then而不重新分配外部变量,请使用Promise.all

app.post("/api/exercise/add", function(req, res, next) {
User
 .findById(req.body.userId)
 .exec()
 .then(function(user) {
   // return the Promise so it can be used by the next then, without nesting
   // because you also need access to `user` in the next then, use Promise.all
   return Promise.all([user, new Exercise({
     description: req.body.description,
     duration: req.body.duration,
     date: req.body.date, //BUG: must add validations, date accepts 19984-01-01
     user: user
   })
   .save()]);
  })
  .then(function([user, exercise]) {
     user.exercises.push(exercise);
     // return the Promise so it can be used by the next then, without nesting:
     return user.save();
   })
   .then(user => res.json({ status: 201, exercises: user.exercises }))
   .catch(err => next(err));
});

请注意

.then(user => user)`

是完全多余的 - 它没有做任何事情,你已经有一个Promise,解决你想要在下一个user.then

另一答案

我们可以这样:

new Promise((resolve, reject) => { 
  let x = 25; 
  if (x%2 === 0) {
    return Promise.resolve('even');
  } else {
    return Promise.resolve('odd');
  }
})
.then(result => {
  console.log('the number is '+result);
});

在这种情况下,条件的两个分支都是同构的,它们都返回一个字符串,结果以相同的方式处理。

但这并不总是发生,例如:

new Promise((resolve, reject) => {
  if (user.type === 'admin') {
    return this.userService.getAdminTools();
  } else {
    return this.userService.getUserTools();
  }
})
.then(result => {
  // What type is the result? Maybe in this case, chaining is not the best solution! 
});

如果你有更多的分支,结果不是同质的,也许链接不是最好的选择。你可以在另一个Promise中监听Promise,或者你可以调用另一个包含异步代码的方法

另一答案

您的执行流现在被分成多个分支,这可能是一个想要的行为。

编写代码时,应始终考虑可重用性和可读性。

其他程序员如何轻松阅读和理解我的代码而不会头疼?

你把它放在一起的方式很难遵循。您应该将要执行的异步操作放入单独的函数中。

将复杂的东西分解为函数是一般的使用方法,不仅仅是在这种特殊情况下。尝试让一个函数做一件事,并有一个执行流程。

User
 .findById(req.body.userId)
 .exec()
 .then(user => user)
 .then(user => asynchronousAddUser(user))
 .catch(err => next(err));
另一答案

它不一定是反模式,但它很大程度上取决于你为什么要这样做。

可能有正当理由打破链条并开始新的链条,但如果你发现自己经常这样做,那么就会出现问题,可能你应该重新考虑你的流程。

我看到人们倾向于开始新连锁的两个常见原因

1.链中某个处理程序根据条件做出决策,每个分支都有完全不同的工作方式。此时启动新链完全有效,但我会创建一个返回promise的新方法。链中的下一个处理程序必须意识到它可能接收异构数据的事实

NewPromise()
.then( res => {
  if (someCond) {
    return OtherPromise(args)
  }
  ....
  return obj
})
.then( res => {
  //this promise must be aware that res may be heterogeneous
})

2.在链中,处理程序会收到一些您无法轻易沿链传播的信息。例如,当需要来自数据库的两条不同信息时,您最终需要两者来完成工作。

User.findById(uid1)
.then(user1 => {
  return User.finById(uid2)
})
.then(user2 => {
 // at this point user1 is not available any more
})

对此的解决方案是在链外部使用变量而不是启动新链

var user1

User.findById(uid1)
.then(user => {
  user1 = user
  return User.finById(uid2)
})
.then(user2 => {
 // at this point user is available and has the value of user1
})

以上是关于在另一个被认为是反模式的承诺中使用承诺?的主要内容,如果未能解决你的问题,请参考以下文章

等待承诺链有啥问题?

了解回调和承诺。它是不是正确?

使用 fetch 时拒绝承诺 [重复]

如果一个已经在运行的 PromiseKit 承诺返回一个未决的承诺的最佳 Swift 模式是啥?

ESLint 报告了我认为没问题的代码的承诺滥用

如何与 forEach 同时执行承诺?