I've a mongoose schema in which a virtual property is defined:
mortSchema.virtual('count').get(function () {
return Pickup.countDocuments({ mortician: this._id }).exec().then((count) => { console.log(count) })
})
I'm rendering it using express like this:
try {
const morticians = await Mortician.find({})
res.render('morticians', {
title: 'Mortician',
morticians: morticians
})
console.log(await morticians.count)
} catch (err) { /* err */ }
Since the promise it not solved at rendering time, it's outputting Promise { <pending> }, before the then() function to the console. How to solve the promise, before passing it to res.render? Where to insert the missing await?
Workaround: Removing the virtual property from the schema like this is working, but not answering my question:
try {
const morticians = await Mortician.find({})
for (mortician of morticians)
await Pickup.countDocuments({ mortician: mortician._id },
(err, count) => { mortician.count = count })
res.render('morticians', {
title: 'Mortician',
morticians: morticians
})
} catch (err) { /* err */ }
Related
Here I am trying to retrieve objects and push them into the array. For some reason there is only one record being pushed into the file when it should contain more objects. Can you help me out with this or let me know where I am going wrong? Here is my code:
exports.createjson = (req, res, next) => {
try {
var myPromise = () => {
// ...
};
var callMyPromise = async () => {
const responsearray = [];
var result = await myPromise();
return new Promise((resolve, reject) => {
result.forEach(element => {
NewsModel.findOne({ _id: element.newsId }).exec(
async (err, result) => {
if (err) {
throw err;
}
reportsModel
.findOne({
$and: [
{ userId: req.query.userId },
{ newsId: element.newsId }
]
})
.exec((err, newsResult) => {
if (err) {
throw err;
}
// console.log(newsResult);
var response = {
newsId: element.newsId,
title: result.title,
collection: result.group,
belivibalityIndex: element.belivibalityIndex,
priorknowledge: element.priorknowledge,
readingTime: element.readingTime,
userId: element.userId,
comment: element.comment,
report: newsResult !== null ? newsResult.feedback : null
};
// #all object pushed and displayed in console
responsearray.push(response);
console.log(response);
console.log(responsearray.length);
// let data = JSON.stringify(responsearray);
// #here is the issue // fs.writeFileSync("abc.json", data, null, null, flag = 'a');
return responsearray;
});
}
);
});
});
};
callMyPromise().then(function(responsearray) {
res.json(responsearray);
});
} catch (error) {
next(error);
}
};
You're not quite using Promises properly. For example, you create a Promise object but never call the resolve/reject functions. In the forEach loop you are calling functions that use callbacks and when that work is done you can resolve the promise you're wrapping it in.
Also you're calling res.json and writing the file (though it's commented out) while you're in the forEach loop. That means res.json will get called multiple times, which is not allowed. You can only have one response from an http request.
I restructured the code so that it collects each promise in an array of Promises then waits for all of them to resolve. Only after all of the work is done, we can write the file and call res.json to complete the http request.
exports.createjson = async (req, res, next) => {
const responsearray = [];
var elements = await myPromise();
var promises = []; // collect a bunch of promises to wait on
elements.forEach(element => {
// one promise per element that resolves when response is on the array
var promise = new Promise(function(resolve, reject) {
NewsModel.findOne({ _id: element.newsId }).exec((err, result) => {
if (err) { return reject(err); }
reportsModel
.findOne({
$and: [{ userId: req.query.userId }, { newsId: element.newsId }]
})
.exec((err, newsResult) => {
if (err) { return reject(err); }
var response = { /* response body */ };
responsearray.push(response);
console.log(response);
console.log(responsearray.length);
// complete the promise now that the response is on the array
return resolve();
});
});
});
// collect each promise in an array so we can wait for them all
promises.push(promise);
});
// wait for all the work to complete
await Promise.all(promises).catch(err => next(err));
// write the responsearray to a file as json
let data = JSON.stringify(responsearray);
fs.writeFileSync("abc.json", data);
return res.json(responsearray);
};
I also removed the try/catch block since the Promise allows you to use .catch in a cleaner way. It simplifies the nesting which makes it easier to read.
The key takeaway here is the general structure:
// get your array to work with
var array = await someFunction()
var manyPromises = []
var manyResults = []
// for each thing in the array create a promise
array.forEach( thing => {
manyPromises.push( new Promise((resolve,reject) => {
doSomething(thing, (err, result) => {
if (err) return reject(err);
// store the results in the array and resolve the promise
manyResults.push(result)
return resolve();
});
});
});
// wait for all promises in manyPromises to complete
await Promise.all(manyPromises).catch(err => return next(err));
// now the many promises are done and manyResponses are ready
saveResponsesToFile(JSON.stringify(manyResponses))
return res.json(manyReponses)
On the following post method, I'm having some issues due to moongose async. res.send(suggestions) is executed first then Expense.findOne.exec
app.post('/suggestions', async function(req, res) {
const suggestions = await req.body.map((description) => {
Expense.findOne({ description: new RegExp(description, 'i') }).exec((err, result) => {
if (result) {
console.log(result.newDescription);
return {
description,
newDescription: result.newDescription,
category: result.category,
subcategory: result.subcategory
};
}
});
});
res.send(suggestions);
});
The result is a array of null values. How can I executed a query for each item, then execute res.send(suggestion)?
Found solution with the following code:
app.post('/suggestions', async function(req, res) {
try {
if (req.body.length > 0) {
const suggestions = req.body.map((description) =>
Expense.findOne({ description: new RegExp(description, 'i') })
);
const results = await Promise.all(suggestions);
return res.send(results);
}
} catch (e) {
console.log('error', e);
}
});
I'm trying to learn how to implement transactions for a simple app in Mongoose with express for a RESTful API.
I've managed to put together a small one change function using the mongoDB session.withTransaction() helper function.
This code block runs with no errors and the document appears in the DB as I would expect.
function createCourse() {
try {
return Course.createCollection()
.then(() => mongoose.startSession())
.then(session => {
session.withTransaction(() => {
return Course.create(
[
{
author: "Larry",
category: "web",
tags: ["tag1", "tag2"],
isPublished: false,
price: 15,
name: "Tester series 3"
}
],
{ session }
);
});
});
} catch (error) {
console.error(error);
}
}
I feel like I'm missing something with how to structure my operations with this function.
Putting everything together into an Express POST method, looks like this works but I'd appreciate a code-review of it.
Also, how can I go about testing that the code is correctly handling errors/roll-backs? Put session.abortTransaction(); somewhere within the sesson.withTransaction() block?
router.post("/", async (req, res) => {
try {
validateRental(req.body);
} catch (error) {
return res.status(400).send(error.details[0].message);
}
let customer = null;
try {
customer = await Customers.findById(req.body.customerId);
} catch (err) {
return res.status(400).send("Invalid customer.");
}
let movie = null;
try {
movie = await Movies.findById(req.body.movieId);
} catch (err) {
return res.status(400).send("Invalid movie.");
}
if (movie.numberInStock === 0)
return res.status(400).send("Movie not in stock.");
try {
mongoose.startSession().then(session => {
session.withTransaction(async () => {
let rental = new Rentals({
renter: {
_id: customer._id,
name: customer.name,
phone: customer.phone,
isGold: customer.isGold
},
movie: {
_id: movie._id,
title: movie.title,
dailyRentalRate: movie.dailyRentalRate
}
});
await rental.save({ session });
movie.numberInStock--;
await movie.save({ session });
res.send(rental);
});
});
} catch (err) {
res.status(500).send(err.message);
}
});
I want to refactor code for chain of promises by async, await. I have sequelize ORM for DB management and the code is written in AWS Lambda function having multiple middleware. In such cases I have to traverse code for multiple entries using sequelize transactions. It is easy to manage using promise.all() but need to change it to async await syntax for cleaner code.
Here are my demo code.
/* get all invoice where user_id === current logged in user, and where status != "paid" */
db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: "paid"
}
}
}).then(invoices => {
if (!invoices || invoices === null) {
return false;
}
function addScheduledTransactionAttempts(invoice, tryPayOnDate, t) {
return new Promise((resolve, reject) => {
/* check If any ScheduledTransactionAttempts exists for this invoice.id */
db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id
}
})
.then(function(attempts) {
if (attempts) {
attempts
.destroy({}, {
transaction: t
})
.then(deletedAttempts => {
console.log("Attempts Record Deleted: ", deletedAttempts);
})
.catch(error => {
reject(error);
t.rollback();
});
}
return db.ScheduledTransactionAttempts.create({
invoice_id: invoice.id,
payment_source_id: PaymentMethodId,
try_pay_on_date: tryPayOnDate,
stripe_customer_id: currentLogInStripeCustomerId
}, {
transaction: t
})
.then(function(attempt) {
resolve(attempt.id);
})
.catch(error => {
reject(error);
t.rollback();
});
})
.catch(error => {
reject(error);
t.rollback();
});
});
}
//Run transaction to addScheduledTransactionAttempts
return db.sequelize.transaction().then(function(t) {
let promiseArr = [];
var i = 0;
invoices.forEach(function(invoice) {
var schedulePaymentDate = moment(paymentDate);
if (invoice) {
let tryPayOnDate = schedulePaymentDate
.add(i, "month")
.format("YYYY-MM-DD");
promiseArr.push(
addScheduledTransactionAttempts(invoice, tryPayOnDate, t) //calling above function
);
i++;
}
});
//now execute promise all
Promise.all(promiseArr)
.then(function(result) {
t.commit();
return true;
})
.catch(function(err) {
t.rollback();
return false;
});
});
});
In the above code I want to change
Promise.all(promiseArr)
which is calling
addScheduledTransactionAttempts
function to do DB queries to simple async function await process to make it easy simpler understandable without having multiple .then or .then inside then promises.
Any help regarding would be appreciated,
Thanks.
It's quite simple. await is valid when invoking methods that return a Promise. All of your SDK methods already return a promise, so refactoring should be quite straight forward. Here's something to get you off ground:
const processInvoices = async currentLoggedInUser {
const invoices = await db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: 'paid',
},
},
});
if (yourOwnLogicForInvoicesObject) {
for (const invoice of invoices) {
const potentiallyFoundInvoice = await db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id,
},
});
if (potentiallyFoundInvoice) {
await addScheduledTransactionAttempts(potentiallyFoundInvoice)
}
}
}
}
const addScheduledTransactionAttempts = async invoice => {
console.log('Do something with your invoice', invoice)
}
Long story short: refactor the code inside your functions into smaller functions and make these new functions async, just like I did with addScheduledTransactionAttempts and processInvoices
More on async/await
I'm using knexjs to query my DB. The problem is, that I want to get some data from the DB and export it so it can be used in other files. there's my code:
async function ebay() {
return new Promise(function(resolve, reject) {
db.select("*")
.from("ebay_site_credentials")
.then(data => {
var z = {
name: data[0].name,
pass: data[0].pass
};
resolve(z);
})
.catch(err => {
console.log(err);
});
});
}
async function app() {
var a = await ebay();
return a;
}
var a = app();
console.log(a);
I want to export clientID, secretID and base64 in a module.exports = {}
but all I get is either undefined or Promise: {}
Any ideas?
Thank you!
Try this
Note return new Promise convert function's behavior to promise.
async function app() {
await ebay().then(DATA => {
console.log(DATA);
return DATA;
}).catch(ERROR => {
console.log('ERROR', ERROR);
retrun ERROR;
});
}
You are probably getting :
Promise { <pending> }
What you can do maybe is something like this :
async function app(cb) {
let a = await ebay();
cb(a);
}
app(a => console.log(a)); //you can export out the a inside the callback