node.js 链接多个承诺(使用猫鼬)
node.js chain multiple promises (with mongoose)
以下是我正在处理的一个典型的 promise 函数。
var _delete = function(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
// ------- (*)
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}))
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
.then(function(d){
// do something
})
}).then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
.then(function(d){
// do something
})
})
} else {
return Promise.reject('this transaction list does not exist');
}
});
};
这看起来不错,直到我处理更复杂的更新/创建代码时才会变得非常混乱。
目前我用 promise 做的是
1. 我有很多无用的 return true 语句,唯一的目的是转到下一个 .then 语句
2. promise 以嵌套的方式编程。此外,输入参数通常很复杂,并且有超过 1 个参数,所以我不能做这样的事情
.then(fun1).then(fun2)
...等等
这让我无法 'tap'
.then
声明 enable/disable 功能。
所以我的问题是如何正确执行此操作?谢谢..
以下是我所说的真正丑陋的事情....
var _process = function(t, tid) {
var that = this;
return Promise.cast(Usermain.find({where: {transaction_id: tid}}))
.bind({}) // --- (*)
.then(function(d){
this.tmain = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
return Promise.cast(Userlist.findAndCountAll({where: {transaction_id: tid}}))
}).then(function(d){
this.tlist = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
if (this.tmain.is_processed) {
return Promise.reject('something is wrong');
}
if (this.tlist.count !== this.tmain.num_of_tran) {
return Promise.reject('wrong');
}
return Promise.resolve(JSON.parse(JSON.stringify(this.tlist.rows)))
.map(function(d){
if (d.is_processed) return Promise.reject('something is wrong with tran list');
return true; // goto next then
});
}).then(function(){
return Promise.cast(this.tmain.updateAttributes({is_processed: 1}, {transaction: t}));
}).then(function(){
return Promise.resolve(this.tlist.rows)
.map(function(d){
var tranlist = JSON.parse(JSON.stringify(d));
return Promise.cast(d.updateAttributes({is_processed: 1, date_processed: Date.now()}, {transaction: t}))
.then(function(d){
if (!d) {
return Promise.reject('cannot update tran main somehow');
} else {
if (tranlist.amount < 0) {
return Usermoney._payBalance(t, tranlist.user_id, -tranlist.amount);
} else {
return Usermoney._receiveBalance(t, tranlist.user_id, tranlist.amount);
}
}
});
});
});
}
在我的项目中我使用 Async.js
我认为您需要将 _process
方法分解为小动作
- 依赖于先前操作结果的操作 - 此处可能使用异步
waterfall
模式
- 不依赖于先前动作结果的动作,它们可以并行执行
- 使用一些自定义流程
这是我的应用程序中的示例:
async.waterfall([
function findUser(next) {
Users.findById(userId, function (err, user){
if(err) {
next(new Error(util.format('User [%s] was not found.', userId)));
return;
}
next(null, user);
});
},
function findUserStoriesAndSurveys(user, next) {
async.parallel([
function findStories(callback) {
// find all user stories
Stories.find({ UserGroups: { $in : user.Groups } })
.populate('Topic')
.populate('Episodes')
.exec(function(err, stories) {
if(err) {
callback(err);
return;
}
callback(null, stories);
});
},
function findSurveys(callback) {
// find all completed surveys
Surveys.find({
User: user
}).exec(function(err, surveys) {
if(err) {
callback(err);
return;
}
callback(null, surveys);
});
}
],
function(err, results) {
if(err) {
next(err);
return;
}
next(null, results[0], results[1]);
});
},
function calculateResult(stories, surveys, next) {
// do sth with stories and surveys
next(null, { /* result object */ });
}
], function (err, resultObject) {
if (err) {
res.render('error_template', {
status: 500,
message: 'Oops! Server error! Please reload the page.'
});
}
res.send(/* .... */);
});
自定义流程请参考Async docs,它确实包含了很多常见的模式,我也在我的客户端中使用了这个库JavaScript。
你可以做两件事:
- Unnest
then
callbacks
- 模块化。这些"do product stuff"和"do inventory stuff"东西可能会成为它们自己的功能(甚至相同?)。
在这种情况下,取消嵌套可以执行以下操作(假设您的注释部分不需要闭包):
function _delete(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}));
else
throw new Error('this transaction list does not exist');
})
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
})
.then(function(d){
// do something
})
.then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
})
.then(function(d){
// do something
});
}
以下是我正在处理的一个典型的 promise 函数。
var _delete = function(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
// ------- (*)
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}))
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
.then(function(d){
// do something
})
}).then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
.then(function(d){
// do something
})
})
} else {
return Promise.reject('this transaction list does not exist');
}
});
};
这看起来不错,直到我处理更复杂的更新/创建代码时才会变得非常混乱。
目前我用 promise 做的是 1. 我有很多无用的 return true 语句,唯一的目的是转到下一个 .then 语句 2. promise 以嵌套的方式编程。此外,输入参数通常很复杂,并且有超过 1 个参数,所以我不能做这样的事情
.then(fun1).then(fun2)
...等等
这让我无法 'tap'
.then
声明 enable/disable 功能。
所以我的问题是如何正确执行此操作?谢谢..
以下是我所说的真正丑陋的事情....
var _process = function(t, tid) {
var that = this;
return Promise.cast(Usermain.find({where: {transaction_id: tid}}))
.bind({}) // --- (*)
.then(function(d){
this.tmain = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
return Promise.cast(Userlist.findAndCountAll({where: {transaction_id: tid}}))
}).then(function(d){
this.tlist = d;
return true; // ---- do nothing, just go to next thennable (is this correct)
}).then(function(){
if (this.tmain.is_processed) {
return Promise.reject('something is wrong');
}
if (this.tlist.count !== this.tmain.num_of_tran) {
return Promise.reject('wrong');
}
return Promise.resolve(JSON.parse(JSON.stringify(this.tlist.rows)))
.map(function(d){
if (d.is_processed) return Promise.reject('something is wrong with tran list');
return true; // goto next then
});
}).then(function(){
return Promise.cast(this.tmain.updateAttributes({is_processed: 1}, {transaction: t}));
}).then(function(){
return Promise.resolve(this.tlist.rows)
.map(function(d){
var tranlist = JSON.parse(JSON.stringify(d));
return Promise.cast(d.updateAttributes({is_processed: 1, date_processed: Date.now()}, {transaction: t}))
.then(function(d){
if (!d) {
return Promise.reject('cannot update tran main somehow');
} else {
if (tranlist.amount < 0) {
return Usermoney._payBalance(t, tranlist.user_id, -tranlist.amount);
} else {
return Usermoney._receiveBalance(t, tranlist.user_id, tranlist.amount);
}
}
});
});
});
}
在我的项目中我使用 Async.js
我认为您需要将 _process
方法分解为小动作
- 依赖于先前操作结果的操作 - 此处可能使用异步
waterfall
模式 - 不依赖于先前动作结果的动作,它们可以并行执行
- 使用一些自定义流程
这是我的应用程序中的示例:
async.waterfall([
function findUser(next) {
Users.findById(userId, function (err, user){
if(err) {
next(new Error(util.format('User [%s] was not found.', userId)));
return;
}
next(null, user);
});
},
function findUserStoriesAndSurveys(user, next) {
async.parallel([
function findStories(callback) {
// find all user stories
Stories.find({ UserGroups: { $in : user.Groups } })
.populate('Topic')
.populate('Episodes')
.exec(function(err, stories) {
if(err) {
callback(err);
return;
}
callback(null, stories);
});
},
function findSurveys(callback) {
// find all completed surveys
Surveys.find({
User: user
}).exec(function(err, surveys) {
if(err) {
callback(err);
return;
}
callback(null, surveys);
});
}
],
function(err, results) {
if(err) {
next(err);
return;
}
next(null, results[0], results[1]);
});
},
function calculateResult(stories, surveys, next) {
// do sth with stories and surveys
next(null, { /* result object */ });
}
], function (err, resultObject) {
if (err) {
res.render('error_template', {
status: 500,
message: 'Oops! Server error! Please reload the page.'
});
}
res.send(/* .... */);
});
自定义流程请参考Async docs,它确实包含了很多常见的模式,我也在我的客户端中使用了这个库JavaScript。
你可以做两件事:
- Unnest
then
callbacks - 模块化。这些"do product stuff"和"do inventory stuff"东西可能会成为它们自己的功能(甚至相同?)。
在这种情况下,取消嵌套可以执行以下操作(假设您的注释部分不需要闭包):
function _delete(t, id) {
return Promise.cast(Event.find({where: {id: id}}, {transaction: t}))
.then(function(d){
if (d) {
return Promise.cast(d.updateAttributes({status: -1}, {transaction: t}));
else
throw new Error('this transaction list does not exist');
})
.then(function(){
// do inventory stuff
return Promise.cast(Inventory.update({}).exec())
})
.then(function(d){
// do something
})
.then(function(){
// do product stuff
return Promise.cast(Product.update({}).exec())
})
.then(function(d){
// do something
});
}