node csv files upload synchronous import followed by mongoose find - node.js

I am not able to get results from a mongoose find query after successfully uploading csv files. The data from the csvs is being saved in my collection OK. But I am getting no results from a following find query. What causes promises to not conform to the sequence? I feel I may be close to understanding promises as they are handled in the event loop but this case escapes me?
let uploadfiles = function (req,res) {
return new Promise( function (resolve, reject) {
upload(req, res, (err) => {
promises = [];
if (!err) {
if (Object.keys(req.files).length>0) {
simpleLoop(req.files, function (value, key, lovey) {
promises.push(insertStreamData(req.files[key]["originalname"]));
});
console.log('hither');
}
}
return Promise.all(promises);
});
resolve('something');
});
};
// insert uploaded csv data from files into db
function insertStreamData(filename){
var originalFileName = filename;
var thefilename = originalFileName.split('.');
var csvfile = "./public/files/"+originalFileName;
var stream = fs.createReadStream(csvfile, { headers: true});
var csvStream = csv().on("data", function(data){
if(data[0] != 'Product #' && data[7] != 0){
var item = new Product({
'Invoice':thefilename[0],
'Product #': data[0],
'SKU': data[1],
'UpcCode': data[2],
'Description': data[3],
'MfgNo': data[4],
'Vendor': data[5],
'Order Qty': data[6],
'Ship Qty': data[7],
'Min Sell': data[8],
'Retail': data[9],
'Cost': data[10],
'Ext Cost': data[11],
'Box': data[12]
});
item.save(function(error){ if(error){ throw error; } });
}
}).on("end", function(){
console.log('the end');
});
stream.pipe(csvStream);
}
let getincomingproducts = function(){
return new Promise(function (resolve, reject) {
resolve('done');
});
};
router.post('/uploaddata', function(req, res) {
uploadfiles(req,res).then(function(result){
return getincomingproducts();
}).then(function(result){
console.log(result);
res.redirect('/showdata');
}).catch(err => console.log("Caught " + err));
});
Output 'done' is logged to the console prior to the first promise. I want to replace done with mongoose find results. But a simple console log demonstrates the unsynchronous result.
done
hither
the end
the end
the end
the end

Please don't mix Promise constructors and such with async and await. This could be a lot easier if you just use await and async functions. And only wrap the callback taking functions once.
A clean version wouldn't have any business logic in the Promise constructors and would used await rather than then in an async function.
I found the second point in the program where it doesn't wait for the completion of an action that is that the insertStreamData does not return a promise that is fulfilled when it is done which is what is causing the out of order issue. I will update my answer.
I see a issue in upload files where it has:
return Promise.all(promises);
});
resolve('something');
which should be
return Promise.all(promises).then(function () {
resolve('something');
});
This change should cause the output to be hither, the end * 4, done. If you want the insertStreamData to occur in serial rather than parallel, that requires a different change.
My version of the script:
function upload_wrapper(req, res) {
return new Promise(function(resolve, reject) {
upload(req, res, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
})
}
let uploadfiles = async function(req, res) {
await upload_wrapper(req, res);
let promises = [];
if (Object.keys(req.files).length > 0) {
simpleLoop(req.files, function(value, key, lovey) {
promises.push(insertStreamData(req.files[key]["originalname"]));
});
console.log('hither');
}
await Promise.all(promises);
return 'something';
};
// insert uploaded csv data from files into db
function insertStreamData(filename) {
return new Promise(function(resolve, reject) {
var originalFileName = filename;
var thefilename = originalFileName.split('.');
var csvfile = "./public/files/" + originalFileName;
var stream = fs.createReadStream(csvfile, {
headers: true
});
var csvStream = csv();
csvStream.on("data", function(data) {
if (data[0] != 'Product #' && data[7] != 0) {
var item = new Product({
'Invoice': thefilename[0],
'Product #': data[0],
'SKU': data[1],
'UpcCode': data[2],
'Description': data[3],
'MfgNo': data[4],
'Vendor': data[5],
'Order Qty': data[6],
'Ship Qty': data[7],
'Min Sell': data[8],
'Retail': data[9],
'Cost': data[10],
'Ext Cost': data[11],
'Box': data[12]
});
item.save(function(error) {
if (error) {
//throw error;
csvStream.pause(); // here the stream should be destroyed if this stops at the first error
reject(error);
}
});
}
}).on("end", function() {
console.log('the end');
resolve('the end');
});
stream.pipe(csvStream);
});
}
let getincomingproducts = function() {
return Promise.resolve('done');
};
router.post('/uploaddata', async function(req, res) {
try {
let result = await uploadfiles(req, res);
let result = await getincomingproducts();
console.log(result);
res.redirect('/showdata');
} catch (err) {
console.log("Caught " + err)
}
});
Untested. It still could be improved.

Related

When trying to write image file it moves ahead and write works late

What i am trying to do here is i am writing a image file after resizing it with node and after that i will use it to upload it. but when i am writing it my uploading code started at that time and file is still not written.
async function getdataFromJimpSmall(image,filePath,FILE_NAME){
try{
var isSucess = false ;
const im = await Jimp.read(image);
return new Promise((resolve, reject) => {
try{
im.resize(150, 215, Jimp.RESIZE_BEZIER, function(err){
if (err) throw err;
});
im.write(filePath+FILE_NAME)
resolve(true)
}
catch(err){
console.log(err);
reject(false);
}
})
}
catch(err){
console.log('getdataFromJimpSmall Err====>'+err)
return false
}
}
I tried to use this but it is getting ahead of my code
it started from here and it is getting called from here.
isThumbnailUrlavailable = await jimpController.imageCoverJimpAndUploadToS3small(getISBNBook, "-#gmail.com", "SaleIsbn")
isThumbnailUrlavailableMedium = await jimpController.imageCoverJimpAndUploadToS3medium(getISBNBook, "-#gmail.com", "SaleIsbn")
And the first function imageCoverJimpAndUploadToS3small() is this:
exports.imageCoverJimpAndUploadToS3small = async (getContent,email,file) =>{
try{
var elementType = 15;
var filePath = path.join(__dirname, '../../uploads/t/')
var FILE_NAME = `${getContent.contentMetaDataRef.isbn}_s.jpg`
var image = getContent.thumbnail
let tempdata = await getdataFromJimpSmall(image,filePath,FILE_NAME)
console.log('temp - ', tempdata);
if(tempdata==true)
{
var data = await commonController.uploadthumbnailTOPublicS3(filePath, FILE_NAME)
console.log('s3', data);
requestImageSize(data.url).then(size =>
{
console.log(size);
if(size.height>0&&size.width>0)
{
tempdata=true;
const getContentElement = ContentElement.findOne({contentId: getContent._id,elementType,elementData: data.keyName}).lean()
if(getContentElement){
ContentElement.findByIdAndUpdate(getContentElement._id, {
createdOn: Date(), modifiedOn: Date(),
}, { new: true })
}
else
{
return tempdata;
}else
{
tempdata=false;
return tempdata;
}
}).catch(err =>
{
console.error(err);
tempdata=false;
return tempdata;
});
}
}
catch(error){
console.log(error)
tempdata=false;
return tempdata;
}
}
But it is not working...
Again i am calling imageCoverJimpAndUploadToS3small() and it starts after that.
as per docs https://www.npmjs.com/package/jimp#writing-to-files-and-buffers, .write runs asynchronously and takes a callback, which is why the promise resolves before image is written
so, you need to use callback, and resolve from there:
im.write(filePath+FILE_NAME, (err)=>{
if(err) throw err;
resolve(true);
});
or, as the docs say, you might use and await .writeAsync, which is promise-based:
await im.writeAsync(filePath+FILE_NAME);
resolve(true);

Not able to return value from promise in Nodejs

I have written the following code in Nodejs which is saving data in MongoDB:
function insertDoc(db,data){
return new Promise(resolve => {
callback=db.collection('AnalysisCollection').insertOne(data).then(function(response,obj){
console.log("Inserted record");
resolve(obj);
//console.log(obj);
// response.on('end',function(){
// resolve(obj);
// });
//return resolve(obj);
}).then(() => { return obj }
).catch(function(error){
throw new Error(error);
});
})
}
I am calling the above function from the main function like this:
async function cosmosDBConnect(nluResultJSON){
try{
//console.log("Inserting to cosmos DB");
console.log(nluResultJSON);
var url = config.cosmos_endpoint;
var result="";
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
// insertDoc(db, data, function() {
result=insertDoc(db, data, function() {
console.log(result);
client.close();
//return data._id;
});
});
}
catch (e) {
console.log(e);
}
}
module.exports = { cosmosDBConnect };
But in cosmosDBConnect, I am getting 'undefined' for the result, though in insertDoc I am getting the output for'obj' with _id for the inserted record.
Please help me to return this _id to cosmosDBConnect.
You are use callbacks inside of async function, which creates internal scopes. So your return aplies to them instead of whole function. You should use Promise-based methods inside of async function using await (without callbacks) or wrap whole function into own Promise otherwise.
Example:
function cosmosDBConnect(nluResultJSON) {
return new Promise((resolve, reject) => {
var url = config.cosmos_endpoint;
var result = '';
var data = JSON.parse(JSON.stringify(nluResultJSON));
MongoClient.connect(url, function(err, client) {
if (err) return reject(err);
assert.equal(null, err);
var db = client.db('NLUAnalysisDB');
insertDoc(db, data).then(obj => {
console.log(obj);
client.close();
return resolve(data._id);
});
});
});
}
Also you need to understand that your insertDoc return Promise and do not accept callback you tried to pass.
Ref: async function
result = insertDoc(db, data).then((data) => {
console.log(data);
}).catch(err => console.error(err));

Aysnc for each callback issue

I am using async for each to achieve some task the problem i am facing is that the final call back never executes
Scenario : i have list of contacts and want to send message to all contacts in parallel and when message is send want to store the response in array and than want to perform some action on final call back
sms.js
function SmsService() {}
SmsService.prototype.sendSms = function(value, callback) {
client.messages
.create({
body: value.body,
from: value.from,
to: value.to
})
.then(message => {
console.log('meesage going', message.sid);
callback(null,message.sid)
})
.catch(e => {
callback(null,'not send')
})
}
module.exports = SmsService;
sender.js
var SmsService = require(path.resolve(__dirname, './sms'));
var smsService = new SmsService();
var data = [{body:'1232324',from:'+12323123',to:'+12312323'},
{body:'112123234',from:'+123123123',to:'+123213123'}, {body:'12sadasdasd34',from:'+112123123',to:'+1223213123'}]
async.forEachOf(data, function (value, i, cb) {
console.log('started',i)
smsService.sendSms(value, function(error, result) {
console.log('sending',i,value.to)//only get result for first item
results.push(result)
cb()
})
}, function (err) {
if (err) console.error(err.message);
console.log('all done')//never executes
console.log(results);//never executes
});
If I move the async part to SMS service it works fine but I want to keep separate the SMS service
You can try something like this.
sms.js
function SmsService() {}
SmsService.prototype.sendSms = function(value, callback) {
return new Promise((resolve, reject) => {
// Do async job
client.messages
.create({
body: value.body,
from: value.from,
to: value.to
})
.then(message => {
console.log('meesage going', message.sid);
resolve(callback(null,message.sid))
})
.catch(e => {
reject(callback(null,'not send'))
})
})
}
module.exports = SmsService;
sender.js
var SmsService = require(path.resolve(__dirname, './sms'));
var smsService = new SmsService();
var data = [{body:'1232324',from:'+12323123',to:'+12312323'},
{body:'112123234',from:'+123123123',to:'+123213123'},
{body:'12sadasdasd34',from:'+112123123',to:'+1223213123'}];
var promises = [];
data.forEach(async function (obj, index) {
console.log('started',index)
promises.push(await smsService.sendSms(obj, function(error, result) {
console.log('sending',i,value.to)//only get result for first item
results.push(result)
}));
});
Promise.all(promises).then(function () {
console.log("Messages sent")
}).catch(function (err) {
console.log("Messages not sent")
})

Incorrect Output, need synchronous for loop

exports.calculateGstBaseOnInput = function(req, res) {
console.log("welcome");
for (var item of req.body.so_items) {
req.productid = item.productid;
req.qty = item.qty;
getItemDetail(req, res).then(function(result) {
return getCartItems(req, res);
}).then(function(result) {
return calculateGST(req, res);
})
}
}
getItemDetail = function(req, res) {
return new Promise(function(resolve, reject) {
console.log("inside getItemDetail");
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
if (err) {
console.log("inside err");
res.json({ status: 'Failure', statusMessage: 'item does not exist' });
} else if (result.length < 0) {
console.log("inside length 0");
res.json({ status: 'Failure', statusMessage: 'item does not exist' });
} else {
req.itemdetail = result;
console.log("price inside getitemdetail= ", req.itemdetail[0].price);
//callback();
}
});
resolve('done');
});
}
getCartItems = function(req, res) {
return new Promise(function(resolve, reject) {
console.log("inside getCartItems");
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
if (err) {
res.json({ status: 'Failure', statusMessage: 'item does not exist' });
} else if (result.length < 0) {
res.json({ status: 'Failure', statusMessage: 'item does not exist' });
} else {
req.cartItems = result;
//callback();
}
});
resolve('done');
});
}
calculateGST = function(req, res) {
return new Promise(function(resolve, reject) {
console.log("inside calculateGST");
if (req.userDetails[0].is_gst_included) {
//total = req.qty * req.itemdetail[0].price;
// console.log("price = ",req.itemdetail[0].price);
//callback();
} else {
//total = req.qty * req.itemdetail[0].price;
//console.log("price = ",req.itemdetail[0].price);
//total = req.qty * req.itemdetail[0].price;
}
resolve('done');
});
}
Actual output :
inside getItemDetail
inside getItemDetail
inside getCartItems
inside getCartItems
inside calculateGST
inside calculateGST
Expected Output(output i wanted):
inside getItemDetail
inside getCartItems
inside calculateGST
inside getItemDetail
inside getCartItems
inside calculateGST
how do i achieve this without setting any time.
You are resolveing immediately instead of resolving within the callback provided to mysqlConnect.query().
Consider an abbreviated version of your getItemDetail function:
function getItemDetail(req, res) {
return new Promise(function(resolve, reject) {
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
// Stuff
});
resolve('done');
});
}
It's logic is:
Create a new promise
Begin a query
resolve()
Whatever called getItemDetail does what it should because getItemDetail resolved
query finishes sometime later
Instead, you should likely be doing something like this, where resolve is within the query callback:
function getItemDetail(req, res) {
return new Promise(function(resolve, reject) {
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
// Stuff
resolve('done');
});
});
}
The logic here is:
Create a new promise
Begin a query
getItemDetail caller shouldn't do anything yet because getItemDetail isn't yet resolved
query finishes at some point and the callback is triggered which calls resolve
getItemDetail caller will now proceed since it has been told that the function resolved
You need to follow this pattern in any function where you need a query to actually finish before the caller should move on.
You should also consider leveraging async/await. Consider this abbreviated example using your code as a base:
const mysqlConnect = {
query(sql, cb) {
setTimeout(() => {
cb(null, ["foo"], ["bar"]);
});
}
};
(async function() {
const t = await calculateGstBaseOnInput({
body: {
so_items: ["a", "b", "c"]
}
});
}());
async function calculateGstBaseOnInput(req, res) {
for (var item of req.body.so_items) {
const itemDetail = await getItemDetail(req, res);
const cartItems = await getCartItems(req, res);
const gst = await calculateGST(req, res);
}
}
function getItemDetail(req, res) {
console.log("getItemDetail");
return new Promise(function(resolve, reject) {
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
// Stuff
resolve('done');
});
});
}
function getCartItems(req, res) {
return new Promise(function(resolve, reject) {
console.log("---getCartItems");
var SQL = "mysql query";
mysqlConnect.query(SQL, function(err, result, fields) {
// Stuff
resolve('done');
});
});
}
function calculateGST(req, res) {
return new Promise(function(resolve, reject) {
console.log("------calculateGST");
// Stuff
resolve('done');
});
}
This outputs:
getItemDetail
---getCartItems
------calculateGST
getItemDetail
---getCartItems
------calculateGST
getItemDetail
---getCartItems
------calculateGST

resolving a promise using mongodb and nodejs

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

Resources