Promises not waiting for each other in my nodejs controller - javascript

I got 4 promises here, and I thought that it would run the first one, then wait until its finished, THEN run the next one, wait till finished and THEN run the next one etc..
But what happens here is that it runs all of them all at once and does not wait for anything to finish.
This is my promise chain:
//
// Run the promises
//
findBanks
.then(findReceipts)
.then(findExpenses)
.then(sendResult)
.catch(err => {
console.error(err);
console.log("getbankAccountReport ERR: " + err);
res.json({error:true,err})
});
This is the output from my console.log
=====findAllBank=====
=====findAllReceipt=====
=====findAllExpense=====
=====RESOLVE findAllBank=====
=====sendResult=====
=====RESOLVE sendResult=====
=====RESOLVE findAllReceipt=====
=====RESOLVE findAllExpense=====
Am I not understanding promises correct or?
Anyway here is my nodejs controller:
exports.getBankAccountReport = function(req, res) {
//
// Find all bank accounts
//
var bankModel = require('../models/bankModel');
var bankTable = mongoose.model('bankModel');
var bankArray = [];
var findAllBank = new Promise(
(resolve, reject) => {
console.log("=====findAllBank=====")
bankTable.aggregate([
...lots of mongo stuff...
],function(err, data) {
if (!err) {
bankArray = data;
console.log("=====RESOLVE findAllBank=====")
resolve(data);
} else {
reject(new Error('findBank ERROR : ' + err));
}
});
});
//
// Find the RECEIPT for each bank account
//
var receiptModel = require('../models/receiptModel');
var receiptTable = mongoose.model('receiptModel');
var receiptArray = [];
var findAllReceipt = new Promise(
(resolve, reject) => {
console.log("=====findAllReceipt=====")
receiptTable.aggregate([
...lots of mongo stuff...
], function (err, data) {
if (!err) {
receiptArray = data;
console.log("=====RESOLVE findAllReceipt=====")
resolve(data);
} else {
reject(new Error('findReceipts ERROR : ' + err));
}
});
});
//
// Find the EXPENSE for each bank account
//
var expenseModel = require('../models/expenseModel');
var expenseTable = mongoose.model('expenseModel');
var expenseArray = [];
var findAllExpense = new Promise(
(resolve, reject) => {
console.log("=====findAllExpense=====")
expenseTable.aggregate([
...lots of mongo stuff...
], function (err, data) {
if (!err) {
expenseArray = data;
console.log("=====RESOLVE findAllExpense=====")
resolve(data);
} else {
reject(new Error('findExpense ERROR : ' + err));
}
});
});
var sendResult = function(data) {
var promise = new Promise(function(resolve, reject){
console.log("=====sendResult=====")
res.json({error:false,
"bank":bankArray,
"receipt":receiptArray,
"expense":expenseArray})
console.log("=====RESOLVE sendResult=====")
resolve();
});
return promise;
};
//
// Run the promises
//
findAllBank
.then(findAllReceipt)
.then(findAllExpense)
.then(sendResult)
.catch(err => {
console.error(err);
console.log("getbankAccountReport ERR: " + err);
res.json({error:true,err})
});
}

You need to wrap your Promises in functions
var findAllBank = function() {
return new Promise(
(resolve, reject) => {
console.log("=====findAllBank=====")
bankTable.aggregate([
...lots of mongo stuff...
],function(err, data) {
if (!err) {
bankArray = data;
console.log("=====RESOLVE findAllBank=====")
resolve(data);
} else {
reject(new Error('findBank ERROR : ' + err));
}
});
});
});
When resolved, the next function in the chain will be called with the data passed in the resolve() function.
Do no confuse the Promise and the function that builds it
When you create a new Promise(executor), you instanciate a new object that will have two methods (functions of an object), .then(resolveCB [, rejectCB]) and .catch(rejectCB).
The aime is to know, whenever a process is done, if it was successful or if it failed, and the continue accordingly.
var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); });
In other words, those methods are used to continue your process once the promise defined by executor is settled. It can either get fulfilled and call the resolveCB callback (usingthen), or rejected and call the rejectCBcallback (using both then and catch). A callback (resolveCB or rejectCB) is a function, not a Promise itself, even if the callback might return a Promise.
myFirstPromise
.then(function resolveCB(result) { console.log(result); }) //you can use a 2nd callback for rejection at this point
.catch(function rejectCB(err) { console.log(err); });
myFirstPromise
.then(
function resolveCB(result) { console.log(result); } // if resolved
, function rejectCB(err) { console.log(err); } // if rejected
)
.catch(function rejectCB(err) { console.log(err); }); // NEVER forget the last catch, just my 2cents :)
We saw the inputs of .then() and .catch() but what about their return value? The both of them will return a new Promise. That's why you can chain the .then()'s and .catch()'s.
myFirstPromise
.then(function resolveCB1(result) { console.log(result); })
.then(function resolveCB2(result) { console.log(result); }) // console.log is called but result is undefined
.catch(function rejectCB1(err) { console.log(err); });
myFirstPromise
.then(function resolveCB3(result) {
throw 'I threw an exception'; // an exception is thrown somewhere in the code
console.log(result);
})
.then(function resolveCB4(result) { console.log(result); })
.catch(function rejectCB2(err) { console.log(err); }); // a promise in the chain get rejected or error occured
In the previous example, we see that our second .then() is hit but result is undefined. The promise returned by first .then() fullfiled but no value as been passed by the executor to the resolve callback resolveCB2. In the second case, an exception occured in resolveCB3, it gets rejected so rejectCB2 is called. If we want our resolve callbacks to receive an argument, we have to notify the exector. To do so, the simplest way is for the callbacks to return a value:
myFirstPromise
.then(function resolveCB1(result) {
console.log(result);
result += ' again';
return result;
})
.then(function resolveCB2(result) {
console.log(result);
return result;
})
.catch(function rejectCB1(err) { console.log(err); });
Now that's said, you've got all the pieces together for understanding a Promise. Let's try to sum it up in a cleaner way:
var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); })
, resolveCB = function resolveCB(result) {
console.log(result);
result += ' again';
return result;
}
, resolveLastCB = function resolveLastCB(result) {
console.log(result);
result += ' and again';
return result;
}
, justLog = function justLog(result) {
console.log(result);
return result;
}
;
myFirstPromise
.then(resolveCB)
.then(resolveLastCB)
.then(justLog)
.catch(justLog);
You can now chan them nicely, it's cool and all
myFirstPromise
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveLastCB)
.then(justLog)
.catch(justLog);
But what if your Promise chain 'really' changes and you need to get rid of myFirstPromise and start with resolveCB instead ? It's just a function, it can be executed but doesn't have any .then() or .catch() method. It's not a Promise. You can't do resolveCB.then(resolveLastCB), it will thow an error resolveCB.then( is not a function or something similar. You might think this is a gross mistake, I didn't call resolveCB and resolveCB().then(resolveLastCB) should work? Unfortunalty for those who thought about that, it's still wrong. resolveCB returns a string, some characters, not a Promise.
In order to avoid this kind of maintenance issue, you should know that the resolve and reject callbacks can return a Promise instead of a value. To do so, we'll use what's called the factory pattern. In simple words, the factory pattern is about instanciating new object using a (static) function instead of using the constructor directly.
var myFirstPromiseFactory = function myFirstPromiseFactory() {
/*
return new Promise(function executor(resolve, reject) {
resolve('resolved');
});
if you just need to resolve a Promise, this is a quicker way
*/
return Promise.resolve('resolved');
}
, resolveFactory = function resolveFactory(result) {
return new Promise(function executor(resolve, reject) {
result = result || 'I started the chain';
console.log(result);
result += ' again';
return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
})
}
, resolveLastFactory = function resolveLastFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
result += ' and again';
return resolve(result);
});
}
, justLogFactory = function justLogFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
return resolve(result);
});
}
;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
.then(resolveFactory)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
justLogFactory('I understand Javascript')
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
Factory functions might come handy when iterating over an array. It might be used to produce an array of promise given an input:
var myFirstPromiseFactory = function myFirstPromiseFactory() {
/*
return new Promise(function executor(resolve, reject) {
resolve('resolved');
});
if you just need to resolve a Promise, this is a quicker way
*/
return Promise.resolve('resolved');
}
, resolveFactory = function resolveFactory(result) {
return new Promise(function executor(resolve, reject) {
result = result || 'I started the chain';
console.log(result);
result += ' again';
return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
})
}
, resolveLastFactory = function resolveLastFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
result += ' and again';
return resolve(result);
});
}
, justLogFactory = function justLogFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
return resolve(result);
});
}
, concatValues = function concatValues(values) {
return Promise.resolve(values.join(' '));
}
, someInputs = [
'I am an input'
, 'I am a second input'
, 'I am a third input'
, 'I am yet an other input'
]
;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
.then(resolveFactory)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
justLogFactory('I understand Javascript')
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Using a factory functions to create an array of promise usable with Promise.all()
var promiseArray = someInputs.map(function(input) {
return justLogFactory(input);
});
Promise.all(promiseArray)
.then(concatValues)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);

Related

Using javascript promises with Mongoose queries

Normally if I was going to run multiple mongoose queries I would use the built in promise to chain them all together. In this case, the user chooses which schemas to search. This could be one of them or both. The following example uses the post data to define which schemas to search and if one is false, it should continue through the promise chain. Right now the final promise is being called before the queries.
Example in my express controller:
app.post('/custom-search', function (req, res) {
var single = false
var multi = false
if(req.body.single){
var single = true
}
if(req.body.multi){
var multi = true
}
var promise = new Promise(function (resolve, reject) {
if(multi){
multiSchema.find({}, function (err, result) {
if(!err){
console.log(result);
resolve()
}
})
}else{
resolve()
}
}).then(function (value) {
if(single){
singleSchema.find({}, function (err, result) {
if(!err){
console.log(result);
resolve()
}
})
}else{
resolve()
}
}).then(function (value) {
console.log("done");
})
})
});
output:
>done
>[singleResults]
>[multiResults]
done should be printing last so that is the first problem.
Like we discussed, few things had to be clean up. First by actually using and returning the promise for it work properly, Second, creating a mini-promise within your first .then() to resolve and reject your single conditional statement. And third, handling/catching promises.
I wrote a pseudo version of your code to illustrate my point of view, hopefully it may be of a good use.
app.get('/custom-search', function (req, res) {
// Manipulating values to test responses
var single = false;
var multi = true;
var promise = new Promise(function (resolve, reject) {
if (multi) {
setTimeout(function () {
resolve('MULTI RESOLVED!');
}, 3000);
} else {
reject('MULTI REJECTED!');
}
})
.then(function (value) {
new Promise(function (resolve, reject) {
if (single) {
setTimeout(function () {
resolve('SINGLE RESOLVED!');
}, 3000);
} else {
reject('SINGLE REJECTED!');
}
})
.catch(function (error) {
console.log('SINGLE ERROR!', error);
})
.then(function (value) {
console.log('SINGLE DONE', value);
});
})
.catch(function (error) {
console.log('MULTI ERROR!', error);
});
return promise;
});

resolving a javascript promise

Below outlines a promise that has two simple steps display on the console something, then display something else after the first step is completed.
I am trying to understand how to resolve the promise, and allow the 2nd step to complete.
var lookup_documents = new Promise(
function(resolve, reject) {
console.log("1st");
//resolve(); - How do I do this outside of this function?
}
);
lookup_documents.then(
function() {
console.log("2nd");
}
);
Normally you use new Promise() when you want to wrap some asynchronous function into a promise, such as:
new Promise((resolve, reject) => {
makeAjaxCall((err, data) => {
if(err) return reject(err);
else return resolve(data);
})
});
There are other Promise implementations, such as q, where you can create a "deferred" object that returns a promise... Something similar would be:
function makeAjaxCallPromise() {
let deferred = Q.defer();
makeAjaxCall((err, data) => {
if(err) return deferred.reject(err);
else return deferred.resolve(data);
});
return deferred.promise;
}
For the specific thing you want... All I can think of is declare a variable outside of the promise function scope and asign it, something like:
let resolver, rejecter;
new Promise((resolve, reject) => {
resolver = resolve;
rejecter = reject;
});
makeAjaxCall((err, data) => {
if(err) return resolver(err);
else return rejecter(data);
});
But that doesn't look too good and can lead to errors... Try to wrap everything in your Promise function.
You can do it like this, using your example - if I understand you correctly.
var lookup_documents = function() {
return new Promise(function(resolve, reject) {
console.log("1st");
resolve();
});
};
lookup_documents().then(function() {
console.log("2nd");
});

how to pass parameters in promise chain

I am really stuck with passing values between promises, I wonder if I can get a help here.
I have a CustomerController calling methods in CustomerRepo
let getCustomers = customerRepo.getCustomers(req.query.offset, req.query.return);
let getProfiles = customerRepo.getProfiles(rows);
getCustomers
.then(function (rows) {
console.log(rows[0]);
})
.then(getProfiles)
I do not know how to pass rows (array of customers) to getProfiles so I can populate the customers object with profiles.
Here is the code in CustomerRepo -
var getCustomers = function (offset, _return) {
return new Promise(function (resolve, reject) {
var sqlString = 'call qsel_customers' + '(' + offset + ',' + _return + ')';
pool.getConnection(function (err, connection) {
if (err) {
return reject(err);
}
connection.query(sqlString, function (err, rows) {
if (err) {
reject(err);
}
else
resolve(rows);
connection.release();
});
connection.on('error', function (err) {
res.json({"code": 100, "status": "Error in connection database"});
});
});
});
}
and for getProfiles
var getProfiles = function (rows) {
return new Promise(function (resolve, reject) {
console.log(rows);
}
}
I get rows undefined. Also please could someone suggest how can I extract the db mysql connection code into a dbConnect.js, to avoid code repetition.
Promise denotes the computation that may fail or not at some point in time. Therefore, in order to create a Promise you do:
return new Promise(function(resolve, reject) {
// here you decide when the computation fails and when succeeds
resolve(1) // success
reject('error') // failed
})
Promise has a method (a swiss-knife, indeed) called then. It takes a function ƒ of one argument. Now, the magic of ƒ looks like this:
if ƒ returns simple value (string, number, array, etc) then it will wrap it in a new Promise and return it. The signature is then : Promise[a] -> (a -> b) -> Promise[b]. Therefore, say you have a function ∂ that returns a Promise of a number and ƒ takes a number and adds "!" to it, you and get this number, pass it to ƒ using then and end up with a new Promise:
ƒ.then(n => n + '!') // Promise[String]
if ƒ returns a Promise, then then will "extract" the value if this Promise, pass it to ∂ and return a new Promise with the result. In pseudocode:
ƒ.then(n => Promise(n + '!')) // Promise[String]
Okay, then returns a Promise. Well, let's design the code:
let getCustomers = () => Promise.resolve([ {}, {}, {} ])
// assume myApi.getProfile takes a Customer and returns a Profile
let getProfiles = (customers) => customers.map(myApi.getProfile)
getCustomers().then(getProfiles).then(console.log.bind(console));
I'm no expert in Promises but i don't think that it is good idea to put pool.getConnection inside your getCustomers promise.
It looks like another promise and should be chained rather then combined.
Getting back to your main quersion. You can do this
getCustomers
.then(function (rows) {
return getProfiles(rows);
})
.then(function() {
console.log('get profiles resolved');
});
and
var getProfiles = function (rows) {
return new Promise(function (resolve, reject) {
resolve(rows);
console.log('other log of rows:' + rows);
}
}
EDITED:
let getCustomers = customerRepo.getCustomers(req.query.offset, req.query.return);
let getProfiles = customerRepo.getProfiles;
getCustomers
.then(function (rows) {
return getProfiles(rows);
})
.then(function(rows) {
console.log('rows number ' + rows);
console.log('get profiles resolved');
);
When chaining Promises, you have to return a Promise resolved with the value you're passing from the upstream function in order to use it in the downstream function.
getCustomers
.then(function (rows) {
console.log(rows[0]);
return Promise.resolve(rows[0]);
})
.then(function(firstRow) {
console.log("The first row's id is " + firstRow.id);
return Promise.resolve(firstRow);
})
.then(getProfiles);
If you need to do asynchronous work in one of the functions, return a new Promise and invoke the resolve/reject functions as appropriate in the asynchronous callback. Promise.resolve() is a shortcut you can use if you're not doing anything asynchronously; it simply returns a Promise object resolved with the value it's passed.

How to return promise.map (bluebird) inside a promise chain?

I'm using a chain to control flow and can't get promise.map in step2() to wait until all of it's generated promises are resolved.
Here's a visual of the flow I'm trying to achieve.
step1()
step2()
step2()
step2()
step2()
step3()
In step2() I'm using Bluebird's promise.map.
Here's the chain:
step1()
.then(function() { return step2() })
.then(function() { return step3() })
.catch(function() { // handle errors })
Here's my functions for each step:
let step1 = function() {
return new Promise(function(resolve, reject) {
// do stuff
resolve()
})
}
let step2 = function() {
return new Promise(function(resolve, reject) {
things = []
return Promise.map(things, function(thing) {
// load file async with callback
})
resolve()
})
}
let step3 = function() {
return new Promise(function(resolve, reject) {
// do stuff
resolve()
})
}
I've tested the chain with many more steps each with a timeout and it worked as expected with the exception of the promise.map in step2(). What am I doing wrong? :)
The problem is from step 2. Promise.map already return promise, so you can just return it to callee. Then you warp the async function with callback with new Promise.
let step2 = function() {
things = []
return Promise.map(things, function(thing) {
// load file async with callback
return new Promise(function(resolve, reject) {
fileAsyncFunc(thing, function callback(err, data) {
if(err) {
reject(err);
return;
}
resolve(data);
});
});
});
}
You are not calling your resolve callback properly.
let step2 = function() {
things = []
return Promise.map(things, function(thing) {
return new Promise( function( resolve, reject ) {
// load file async with callback
resolve(...)
} )
})
}
In your case resolve() is always called, since you are executing async function for loading the file.

Why doesn't this test with promises pass?

I've stepped into wonderful world of Promises a few days ago and I was just thinking I was enlightened. Promises look simple but they can be confusing.
Could you please tell me why the following test doesn't pass?
var Promise = require('bluebird');
var expect = require('chai').expect;
var request = Promise.promisifyAll(require('request'));
describe('Promise', function() {
it('should work again', function() {
var final_result;
function first_promise() {
return new Promise(function(resolve, reject) {
resolve("http://www.google.com");
})
}
function second_promise() {
return new Promise(function(resolve, reject) {
resolve("This is second promise!");
})
}
function inner_async_request(url_from_first_promise) {
return new Promise(function(resolve, reject) {
return request.getAsync(url_from_first_promise).spread(function(response, content) {
final_result = content;
resolve(content);
})
})
}
return request.getAsync('http://127.0.0.1:3000/').spread(function(result, content) {
//do something with content and then return first_promise
console.log(content);
return first_promise;
})
.then(function(url) {
inner_async_request(url).then(function(result) {
console.log(result);
final_result = result;
})
return second_promise;
})
.then(function(result) {
// result should be "This is second promise!"
console.log(result);
// final_result should be google's html
expect(final_result).not.to.be.undefined;
})
});
});
Currently the error is: Unhandled rejection Error: options.uri is a required argument which should be received from first_promise, I guess?
By this test, actually, I want to understand how to use promises which depend on each other AND how to use promises as asynchronous functions inside promises -which should just work separately-.
Thank you
You need to call the functions to return promises, like
return request.getAsync('http://localhost:3000/').spread(function(result, content) {
//do something with content and then return first_promise
console.log(content);
return first_promise();
})
And in some cases you don't need to create new promises at all
for Eg.
function inner_async_request(url_from_first_promise) {
return request.getAsync(url_from_first_promise).spread(function(response, content) {
final_result = content;
return content;
})
}
Finally, to make your test work, you need to modify this as well
.then(function(url) {
// return
return inner_async_request(url).then(function(result) {
console.log(result);
final_result = result;
return second_promise(); // return second promise inside then
})
})
DEMO

Categories