我有一个代码如下:
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
})