Node.js chain of several promises (with mongoose)

The following is a typical promise function I'm dealing with.

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

This looks fine until I come across a more complicated update / code generation that will become really messy.

I currently promise that 1. I have many useless return true statements, and my only goal is to move on to the next statement .then 2. the promise is programmed in a nested style. also input arguments are usually complex and have more than 1 argument, so i can't do something like this

.then(fun1).then(fun2)

... etc.

which makes it impossible to execute the .then operator 'tap' to enable / disable the function.

So my questions are: how do I do this right? Thanks..


the following really ugly things i'm talking about ....

 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); } } }); }); }); } 
+6
source share
2 answers

You can do two things:

  • Cancel then callbacks
  • modularizes. These “product things” and “do inventory things” can become their own functions (or even the same?).

In this case, unsesting can do the following (assuming you don't need closures in your comments):

 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 }); } 
+7
source

In my projects I use Async.js

I think you need to decompose your _process method into small actions

  • Here, actions that depend on the result of previous actions can be used - the async waterfall template.
  • Actions that are independent of the outcome of previous actions can be performed in parallel
  • Use own process

Here is an example from my application:

 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(/* .... */); }); 

Refer to the Async docs for the user process, it does contain a lot of common templates, I also use this library in my client-side JavaScript.

+2
source

Source: https://habr.com/ru/post/980969/


All Articles