Task : Get query from database => generate multiple pdf => send in mail attachment via node js
Problem: the problem here is before pdfs are generated my sendMail() function called. So how to call sendMail function only after all pdfs generated?
pdfs = [];
// Pdf generate code
var generatePdf = function (rows) {
return new Promise(function (resolve, reject) {
for (var i = 0; i < rows.length; i++) {
html = ejs.renderFile(
'views/voucher.ejs',
{
voucher: rows[i]
},
function (error, success) {
if (error) {
console.log(error);
} else {
var pdf_path = '/srv/ voucher/uploads/voucher/' + Math.random().toString(36).substring(7) + '.pdf';
htmlToPdf.convertHTMLString(success, pdf_path, function (error, success) {
if (error) {
console.log('Oh noes! Errorz!');
console.log(error);
} else {
pdfs.push(pdf_path);
console.log("Single");
console.log(pdfs);
console.log('Woot! Success!');
}
});
}
});
}
resolve();
});
}
// send mail code
var sendMail = function () {
new Promise(function (resolve, reject) {
console.log("All" + pdfs);
pdfs.forEach(function (value, key) {
// mail sending code
});
});
};
var findVoucherAndSendMail = function () {
return new Promise(function (resolve, reject) {
var query = con.query('SELECT * FROM voucher limit 20', function selectAll(err, rows, fields) {
if (err) {
throw err;
}
resolve(rows);
});
});
};
findVoucherAndSendMail().then(function (fromResolve) {
return generatePdf(fromResolve);
}).then(function () {
return sendMail();
}).catch(function () {
});
Your problem is link to an asynchronous behavior in your for loop when generating pdf.
Your promise is resolved before your pdfs are generated.
You should use Promise.all to wait for generation completion.
function generatePdf(rows) {
return Promise.all(rows.map(row => asyncPdfGeneration(row));
}
function asyncPdfGeneration(row) {
return new Promise(function (resolve, reject) {
...generate pdf
resolve(pdf);
}
}
The 2nd 'then' is on your function findVoucherAndSendMail(), so both generatePdf() and sendMail() will fire once findVoucherAndSendMail() is finished.
Place the 2nd 'then' on generatePdf()
findVoucherAndSendMail().then(function (fromResolve) {
return generatePdf(fromResolve).then(function () {
return sendMail();
});
}).catch(function () {
});
edit
Did you try: resolve(pdfs); instead of just resolve();
Related
If I have a sequence of promises/function as example below, is it still possible that functions would resolve out of sequence? I have one function that is a mongoose query and update then another sends the data to a view. My view is loading but is not reflecting the correct results of the mongoose query results.
let function_one(data) = function(){
return new Promise( function(resolve, reject) {
{do stuff...
resolve('howdy');
}
});
};
let function_two(some_data) = function(){
return new Promise( function(resolve, reject) {
{ do stuff...
resolve('howdy');
}
});
};
let function_three(some_data) = function(){
return new Promise( function(resolve, reject) {
{do stuff...
resolve('howdy');
}
});
};
let function_four(some_data) = function(){
return new Promise( function(resolve, reject) {
{do stuff...
resolve('howdy');
}
});
};
function_one(data).then(function(result){
return function_two(result);
}).then(function(result){
return function_three(result);
}).then(function(result){
return function_four(result);
}).then(function(result){
console.log(result);
}).catch(err => console.log("Caught " + err));
Here's the code I have put together: It is as if "res.json({status: 200, data: result});" is being executed before the mongoose find completes?
let gettheinformation = function(invoicelist3){
return new Promise( function(resolve, reject) {
// console.log(invoicelist3);
Product.aggregate([
{
$match: {
Priced: true
}
}
,
{
$group: {
_id: "$Invoice",
pricedcount: {$sum: 1}
}
}
], function (err, result) {
if (err) {
console.log(err);
} else {
resolve(result);
}
});
});
};
let getinvprodspriced = function(invlist){
return new Promise( function(resolve, reject) {
// console.log(invlist);
for(var oo = 0; oo < invlist.length; oo++){
Invoicestatus.update({Invoice:invlist[oo]._id}, {Noofitemspriced: invlist[oo].pricedcount}, {upsert: true}, function (err) {});
}
resolve(invlist);
});
};
let getinvprodcount = function(invprodcount){
return new Promise( function(resolve, reject) {
Product.aggregate([
{
$group: {
_id: "$Invoice",
pradcount: {$sum: 1}
}
}
], function (err, result) {
if (err) {
console.log(err);
} else {
// console.log(result);
resolve(result);
}
});
});
}
let saveinvprodcount = function(invprodcount){
return new Promise( function(resolve, reject) {
for(var ok = 0; ok < invprodcount.length; ok++){
Invoicestatus.update({Invoice:invprodcount[ok]._id}, {Noofitems: invprodcount[ok].pradcount}, {upsert: true}, function (err) {});
}
resolve(invprodcount);
});
};
let getarrdocs = function(result){
return new Promise( function(resolve, reject) {
Invoicestatus.find({}, function(err, docs){
resolve(docs);
});
});
};
router.get('/fetcharrdata', function(req, res) {
gettheinformation().then(function(result){
return getinvprodspriced(result);
}).then(function(result){
return getinvprodcount(result);
}).then(function(result){
return saveinvprodcount(result);
}).then( function(result){
return getarrdocs(result);
}).then(function(result){
res.json({status: 200, data: result});
}).catch(err => console.log("Caught " + err));
});
The problem is in saveinvprodcount methods for loop, function will resolve before execution of Invoicestatus.update function,
let saveinvprodcount = function(invprodcount){
return new Promise( function(resolve, reject) {
for(var ok = 0; ok < invprodcount.length; ok++){
Invoicestatus.update({Invoice:invprodcount[ok]._id}, {Noofitems: invprodcount[ok].pradcount}, {upsert: true}, function (err) {});
}
//this will resolve before completion of Invoicestatus.update
resolve(invprodcount);
});
};
First promisify update function then use an array to hold promises and You can use promise.all to execute all promises at once. See below
let saveinvprodcount = function (invprodcount) {
let promises = [];
for (var ok = 0; ok < invprodcount.length; ok++) {
promises.push(updateData(invprodcount[ok]));
}
return Promise.all(promises);
};
let updateData = function (invProd) {
return new Promise(function (resolve, reject) {
Invoicestatus.update({ Invoice: invProd._id },
{ Noofitems: invProd.pradcount }, { upsert: true }, function (err) {
if (err)
reject(err);
else
resolve();
});
});
};
Yes. You can do. by using Promise.all([promise1, promise2, promise3]) to let them running asynchronously and get the result at once. However, in this case, the result of the first call is the input, you need to wait for it before running the second one. So that, you have to run in sequence (because of the business logic, not the nature of the language.
How can we return a value from promise rather than console.log? If i change console to return(values[1] - values[0]). And try to do console.log(balance(user)), it prints Promise { <pending> }.
I don't know if i am doing it right or not, what am i trying to do is get balance of user by subtracting all purchases from all Deposit/withdrawals. Doing without promises, it returns value before the DB queries are completed. Any better solution?
function totalPurchases(user) {
return new Promise(function (resolve, reject) {
Purchase.getTotalPurchases(user, function (err, tot) {
if (err) {
// console.log(err);
reject(new Error("Error: 1002"));
// return false;
}
else {
resolve(tot[0].purchases);
}
})
})
}
function totalDW(user) {
return new Promise(function (resolve, reject) {
DepWid.getTotalDepwids(user, function (err, tot) {
if (err) {
reject(new Error("Error: 1003"));
}
else {
resolve(tot[0].depWids);
}
})
})
}
exports.balance = async (user) => {
let promises = [];
promises[0] = totalPurchases(user);
promises[1] = totalDW(user);
Promise.all(promises)
.then(function (values) {
console.log(values[1] - values[0]);
//Return this rather than console.log
})
.catch(function (err) {
console.log(err);
})
}
Since balance is an async function, you can await Promise.all and then later return the result.
function totalPurchases(user) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(5);
}, 2000);
})
}
function totalDW(user) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(3);
}, 1000);
});
}
let balance = async (user) => {
let promises = [];
promises[0] = totalPurchases(user);
promises[1] = totalDW(user);
const [deposits, withdrawals] = await Promise.all(promises);
return deposits - withdrawals;
}
balance().then(r => { //If `balance' is called from an async method you can do await here. Else you need to do `then` like I have done here
console.log(r); //prints 2
});
Hello I am new to nodejs and mongodb, i am having trouble resolving my first promise after the second promise has been resolved. I can see the result of my second promise after it is resolved. Here is my code
var getShows = function () {
return new Promise(function (resolve, reject) {
usersdb.find(function (err, result) {
if(err) return console.error(err);
var usersFromCall = result;
var task = function (users) {
return new Promise(function (resolve, reject) {
var user = {
'name': '',
'pages': []
}
user.name = users.show;
console.log(users);
pagedb.find({'show' : user.name}, function (err, resp) {
for(var j = 0 ; j <resp.length; j ++){
var pages = { 'pageId': ''};
pages.pageId = resp[j].pageId;
user.pages.push(pages);
}
console.log(user);
resolve({show: user});
})
});
};
var actions = usersFromCall.map(task);
return Promise.all(actions);
}).then(function () {
resolve()
})
});
};
do i resolve the first promise in a then function after the find?
The following should work:
var getShows = function () {
return new Promise(function (resolve, reject) {
usersdb.find(function (err, users) {
if (err) return console.error(err);
var task = function (user) {
return new Promise(/* ... */);
};
var actions = users.map(task);
Promise.all(actions).then(resolve, reject);
});
});
};
getShows().then(function (results) {
// Prints the result of each task as an array
console.log(results);
});
Looking at your code, it seems .find returns a Promise. So, just for the sake of avoiding the Promise constructor anti-pattern please try the following, I believe it will produce the correct result
var getShows = function() {
return usersdb.find()
.then(result =>
Promise.all(result.map(
users =>
pagedb.find({
show: users.show
})
.then(resp => ({
show: {
name: users.show,
pages: resp.map(item => ({pageId: item.pageId}))
}
}))
)
)
);
};
or the ES5 version
var getShows = function getShows() {
return usersdb.find().then(function (result) {
return Promise.all(result.map(function (users) {
return pagedb.find({
show: users.show
}).then(function (resp) {
return {
show: {
name: users.show,
pages: resp.map(function (item) {
return { pageId: item.pageId };
})
}
};
});
}));
});
};
In a gulp task I have the following code that creates an array of gitAction promises that get executed within a Promise.all() statement. Afterwards, I'm calling a further statement in a then(). But the then() is being called before the git pulls in the all() have terminated. Any clues please?
var git = require('gulp-git');
var gitActionPromise = function(repo, url) {
console.log('git action '+repo);
var pathToRepo = './repos/'+repo;
if (fs.lstatSync(pathToRepo).isDirectory()) {
return new Promise((resolve, reject) => {
git.pull('origin', 'master', {cwd: pathToRepo}, function (err) {
console.log(repo + " pull done!");
if (err) {
console.log('error');
reject(err);
} else {
console.log('ok');
resolve();
}
})
})
} else {
return new Promise((resolve, reject) => {
git.clone(url, {cwd: pathToRepo}, function (err) {
console.log(repo + " clone done!");
if (err) {
console.log('error');
reject(err);
} else {
console.log('ok');
resolve();
}
})
})
}
};
var repos = package.repos || {};
var promises = Object.keys(repos).map(function(repo) {
return gitActionPromise(repo, repos[repo]);
});
Promise.all(promises).then(
console.log('something else') <= this line was causing my issue
); needed to be enclosed in function
You have to pass a function to then:
Promise.all(promises).then(function() {
console.log('something else');
});
The code you have simply logs "something else" right away.
In the below code I need to call function1() and then function2().
Both functions do some asynchronous operations in it. So I wanted it to be called one after the other.
There is a mysql connection object that will be returned as a promise. So it has to be used in entire program finally closed at the end.
When I run the below code I don't get output it just stays blank and trying to debug it never hits function1.
constants.js
var Promise = require('bluebird');
module.exports = {
getMySqlConnection: function () {
var conObj = { host: "localhost", user: "root", password: "", database: "deccan" };
var connection = require("mysql").createConnection(conObj);
return new Promise(function (resolve, reject) {
connection.connect(function (error) {
if (error)
reject(error);
else
resolve(connection);
});
});
}
};
testing.js
var constants = require("./constants.js");
var Promise = require('bluebird');
var mysqlConnection = null;
constants.getMySqlConnection().then(function (connection)
{
mysqlConnection = connection;
return new Promise(function1);
})
.catch(function (e)
{
console.log(e);
}).then(function (result)
{
console.log("Finished Everything");
});
function function1()
{
return new Promise(function (resolve, reject)
{
var sql = "select *from employee";
mysqlConnection.query(sql, function (error, results)
{
if (error)
reject(error);
else
resolve(results);
});
}).then(function (value)
{
return new Promise(function (resolve, reject)
{
// do something here
});
});
}
function function2()
{
return new Promise(function (resolve, reject)
{
var sql = "select *from management";
mysqlConnection.query(sql, function (error, results)
{
if (error)
reject(error);
else
resolve(results);
});
}).then(function (value)
{
return new Promise(function (resolve, reject)
{
// do something here
});
});
}