Javascript 在另一个承诺中使用承诺是否被视为反模式?

Javascript 在另一个承诺中使用承诺是否被视为反模式?,javascript,promise,Javascript,Promise,我有一个如下代码: 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.b

我有一个如下代码:

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
.catch
处理程序相同,则只需
中返回
新承诺。然后
将其传递到下一个
。然后
catch
,如下代码所示

要将多个变量/承诺传递给下一个
。然后
而不重新分配外部变量,请使用
承诺。所有

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)`

是完全多余的-它没有任何作用,您已经有了一个承诺,可以在下一个
中解析为您想要的
用户

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! 
});

如果您有更多的分支,并且结果不一致,那么链接可能不是最佳选择。您可以在另一个承诺中侦听该承诺,也可以只调用另一个包含异步代码的方法

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

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

其他程序员如何轻松地阅读和理解我的代码而不感到头痛

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

将复杂的东西分解成函数是一种普遍使用的好方法,而不仅仅是在这种特殊情况下。尝试使用一个函数来完成一件事,并使用一个执行流

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

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

打破这个链条并开始一个新的,可能有合理的理由,但是如果你发现自己经常这样做,那么事情就错了,也许你应该重新考虑你的流程

我发现人们倾向于建立新连锁店的两个常见原因

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
})
1.处理程序在链中的某个点根据条件做出决策,每个分支都有完全不同的工作方式。在这一点上,启动一个新的链是完全有效的,但是我将创建一个新的方法来返回一个承诺。链中的下一个处理程序必须知道它可能接收异构数据这一事实

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
})

这的确是一种气味。更多的气味是火,当你保存时忘记你正在表演。你应该考虑使用@ SpEnter,我希望在下一个捕捉中会发现最终的错误,或者他们不会吗?如果你<代码>返回用户.SaveE()(用户= > RES.JSON({状态:201,练习:用户。练习}))
然后值/错误将流经承诺链。如果不返回承诺,您将在保存过程中跳过捕获。
。然后(函数(练习){user.exerces.push(练习)}
引发“用户未定义”。用户对象似乎不在练习承诺的范围内。我可以在
findById
完成时使用一些临时变量来存储用户,然后使用它来推动练习。是否有任何方法来解决此异构返回,使用户在返回练习对象后可用?是的,您可以使用
Promise.all
要沿承诺链传递多个值而不创建不必要的外部变量,请参见编辑