i'm a beginner in Nodejs.
I'm trying to use async/ await to create an api:
getAllTasks: function (callback) {
return db.query("Select * from tasks", callback);
},
function asyncGetAllTask(){
return new Promise((resolve,reject)=>{
resolve (Task.getAllTasks());
})
}
router.get('/:id?', async function (req, res) {
if(req.params.id){
await asyncGetTaskByID(req)
.catch(err=>{
res.send(400, {status:400, message: 'bad request', type:'client errors'});
})
.then(rows=>{
res.json(rows)
})
}
else {
await asyncGetAllTask()
.catch(err=>{
res.json(err);
})
.then(rows=>{
res.json(rows);
})
}
});
I'm trying to get all "tasks" from the database (i'm using mysql). However, i don't get the tasks rows. Instead, i get this when i use a get method from postman:
{
"domain": null,
"_events": {},
"_eventsCount": 0,
"_callSite": {},
"_ended": false,
"_idleNext": null,
"_idlePrev": null,
"_idleStart": null,
"_idleTimeout": -1,
"_repeat": null,
"sql": "Select * from tasks",
"typeCast": true,
"nestTables": false,
"_resultSet": null,
"_results": [],
"_fields": [],
"_index": 0,
"_loadError": null
}
Am i missing some thing? I don't understand somehow i got the wrong result.
Sorry for my English.
Thank you
The combination of the await keyword, with the promise interface and handlers (ie .catch() and .then()) looks in correct here.
If you want to use await, you should restructure your code along the lines of this (note the use of try and catch, to achieve the catch-like behaviour that you want for error handling):
router.get('/:id?', async function (req, res) {
try {
if(req.params.id){
// Correct use of await typically does not require the use of .then()
// and is as follows
const rows = await asyncGetTaskByID(req)
res.json(rows)
}
else {
// Correct use of await typically does not require the use of .then()
// and is as follows
const rows = await asyncGetAllTask()
res.json(rows);
}
}
catch(err) { // Catch block to return error result
res.send(500, {status:500, message: 'an error occurred'});
}
});
Additionally, you will likely need to update your method asyncGetAllTask() to the following:
function asyncGetAllTask(){
return new Promise((resolve,reject)=>{
// The resolve callback should probably be passed to getAllTasks
// as per your defintion of Task.getAllTasks
Task.getAllTasks((error, rows) => {
if(error) {
reject(error);
} else {
resolve(rows);
}
});
})
}
Related
I am creating a document in my MongoDB database using Mongoose with the following code:
workoutsRouter.post('/', async (req, res) => {
await mongoose.connect('mongodb+srv://nalanart:<password>#cluster0.2iplh.mongodb.net/workout-app-db?retryWrites=true&w=majority',
{ useNewUrlParser: true, useUnifiedTopology: true })
await Workout.create({
mains: req.body.mains,
accessories: req.body.accessories
}, err => {
if(err) {
throw err
} else {
res.sendStatus(201)
}
})
})
My problem is that it does not send a 201 status as I have written it to. Any idea why? It just says Sending... on Postman
And here it appears in my collection:
Yeah, because you are both awaiting and passing a callback. You are mixing the await and callback syntaxes. If you pass Mongoose a callback, it won't return a Promise, so it will be awaited forever, since it will never resolve. Either await it, or pass it a callback, not both. Also, try res.status(201).end()
try {
await Workout.create({
mains: req.body.mains,
accessories: req.body.accessories
});
res.status(201).end();
} catch (err) {
throw err
}
Here i am trying to retrieve all the objects and push them into the json file. For some reason there is only one record being pushed into file when it should contain more objects. The response is being sent even before the execution. Can you help me out with this or let me know where I am going wrong? Here is my code:
exports.createjoson = (req, res) => {
const Responsearray = [];
async.waterfall(
[
function(waterfallCb) {
// ... first function
},
function(results, waterfallCb1) {
//second function
async.eachLimit(
results,
100,
function(singleResult, eachCallback) {
async.waterfall(
[
async function(innerWaterfallCb) {
try {
NewsModel.find(
{ _id: singleResult.newsId }, // #individual article
async (err, newsResult) => {
if (err) {
return innerWaterfallCb(
// #displaying error
"error in fetching news data"
);
}
const map = new Map();
for (const item of newsResult) {
if (!map.has(item.source)) {
map.set(item.source, true);
Response = {
newsId: item._id,
title: item.title,
comment: singleResult.comment
};
}
}
resPond = await Response;
Responsearray.push(resPond);
let data = JSON.stringify(Responsearray);
await fs.writeFileSync("abc.json", data);
}
);
} catch (error) {
innerWaterfallCb(error);
}
}
],
function(err) {
if (err) {
return eachCallback(err);
}
eachCallback(null);
}
);
},
function(err) {
if (err) {
return waterfallCb1(err);
}
waterfallCb1(null);
}
);
}
],
function(err) {
if (err) {
return res.status(200).json({ status: "400", message: err });
}
res.status(200).json({ status: "200", message: "success" });
}
);
};
There are a number of problems with the code:
fs.writeFileSync will overwrite the file, not append to it, so only the last data you write will be in abc.json. Also it does not return a Promise so there is no need to use await on it. It runs synchronously so will not return until it's complete (that's what the Sync in its function name means). To append instead of overwrite the file, you can set the flag option to "a" to append (the default is "w").
There doesn't seem to be a call to return innerWaterfallCb(null) anywhere - only in error conditions. The inner waterfall function shouldn't be marked async since it doesn't need to do any await calls really. But you should call return innerWaterfallCb(null) after the file is written.
It may be better to just collect the data in responseArray and write the file once at the end of the outer waterfall instead of writing it repeatedly deep inside the inner waterfall.
Variables should start with lowercase letters (like responseArray not ResponseArray since uppercase first letters indicate classes or modules usually.
Don't mix async/await with the async module (waterfall and eachLimit). If you're using proper Promises and async/await then there should be no need to use the async module. It would be cleaner to remove the use of waterfall entirely and rewrite to use Promise objects properly.
I have router and two acync fetching from DB. What is the correct way to catch errors here?
router.get('/restaurant/:id', async (req, res, next) => {
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
var products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
});
Either:
try {
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
var products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
} catch (err) {
// Handle your errors here...
}
which is your typical pattern. Depending on if you can recover and continue with the rendering (with a default value or something), you might want to enclose only the await calls in the try block (and declare the variables before the try block).
Otherwise, you're awaiting promises, so you can still use the promise .catch method if that seems cleaner to you.
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec()
.catch(err => { /* handle error here*/ });
var products = await Product.find({restaurant: req.params.id}).exec()
.catch(err => { /* handle error here*/ });
However, this pattern is only going to be useful if you can still return something valid in your catch code (eg, some kind of default or fallback value). If you need to bail out of the whole thing in the event of an error, this won't work for you, and you should use the previously mentioned try/catch pattern instead.
If you're going to use await, then you would use try/catch to catch a rejection of either of the promises you were awaiting:
router.get('/restaurant/:id', async (req, res, next) => {
try {
let current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
let products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
} catch(e) {
// do whatever you want
console.log(e);
res.sendStatus(500);
}
});
You can compare that to regular promise programming using .then():
router.get('/restaurant/:id', async (req, res, next) => {
Promise.all([
Restaurant.findOne({restaurantId: req.params.id}).exec(),
Product.find({restaurant: req.params.id}).exec()
]).then([current_restaurant, products] => {
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
}).catch(e => {
// add your desired error handling here
console.log(e);
res.sendStatus(500);
});
});
Since your two database requests are independent of one another, the second version allows the DB queries to be parallelized and may actually run slightly faster (depends upon your database).
Code snippet below is using sails waterline ORM to make DB queries and sending response. However, the execution flow is weird, The code outside of map function is running before the map function has finished executing. 'I am outside of map' is being printed in the console before 'I am inside of map'. I think it can be solved this using Promise or async / await. I have tried using Promise.all() below, but it doesn't work, the response is always an empty array. I would be grateful if you could give an example on how to solve this kind of issues.
allMembers: (req, res) => {
const projectId = req.params.id;
ProjectMembers.find({projectId: projectId}).exec( (err, members) => {
if(err) res.serverError("bad request!");
if(members.length === 0) res.notFound({message: "No members are found for this project!"});
let membersInfo = [];
let promise = Promise.all(members.map(m => {
User.findOne({id: m.userId}).exec( (err, user) => {
if(err) membersInfo.push({name: null, userId: null, email:null,rate:null, error: 'Internal error!'})
else if(!user) membersInfo.push({name: null, userId: null, email:null,rate:null, error: 'No user found'})
else membersInfo.push({name: user.name, userId: user.id, rate: m.rate, error: null})
console.log("i am inside of map");
})
}));
console.log("I am outsie of map")
promise.then( (resolve) => {return res.ok({members: membersInfo})});
}
I was about to tell you "don't use queries in .map", but on looking, I think your code is quite close to working. The argument of Promise.all has to be an array of promises. Each User.findOne is indeed a promise - the stumbling block is that once you use .exec it no longer returns a promise.
I think the answer is to do your processing inside the .then instead of right inside the .map:
ProjectMembers.find({projectId: projectId}).exec( (err, members) => {
if(err) return res.serverError("bad request!");
if(members.length === 0) return res.notFound({message: "No members are found for this project!"});
let promise = Promise.all(members.map(m => User.findOne({id: m.userId})));
promise.then( (values) => {
// values is the array of user objects returned from the array of queries
let membersInfo = values.map((user) => {
if (!user) {
return {name: null, userId: null, email:null,rate:null, error: 'No user found'};
} else {
return {name: user.name, userId: user.id, rate: m.rate, error: null};
}
});
return res.ok({members: membersInfo});
}, (err) => {
return res.serverError("Error finding users");
});
The promise only has a single fail callback, so you lose the ability to individually catch and handle querying errors (though you can still individually handle not-found results).
im using nodejs 8. I've replaced promise structure code to use async and await.
I have an issue when I need to return an object but await sentence resolve undefined.
This is my controller method:
request.create = async (id, params) => {
try {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
Property.findById(id).then( async (property) => {
property.requests.push(request._id)
await property.save()
let response = {
status: 200,
message: lang.__('general.success.created','Request')
}
return Promise.resolve(response)
})
}
}
catch (err) {
let response = {
status: 400,
message: lang.__('general.error.fatalError')
}
return Promise.reject(response)
}
}
In http request function:
exports.create = async (req, res) => {
try {
let response = await Request.create(req.params.id, req.body)
console.log(response)
res.send(response)
}
catch (err) {
res.status(err.status).send(err)
}
}
I tried returning Promise.resolve(response) and Promise.reject(response) with then and catch in the middleware function and is occurring the same.
What's wrong?
Thanks a lot, cheers
You don't necessarily need to interact with the promises at all inside an async function. Inside an async function, the regular throw syntax is the same as return Promise.reject() because an async function always returns a Promise. Another thing I noticed with your code is that you're rejecting promises inside a HTTP handler, which will definitely lead to unexpected behavior later on. You should instead handle all errors directly in the handler and act on them accordingly, instead of returning/throwing them.
Your code could be rewritten like so:
request.create = async (id, params) => {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
let property = await Property.findById(id)
property.requests.push(request._id)
await property.save()
}
}
And your http handler:
exports.create = async (req, res) => {
try {
await Request.create(req.params.id, req.body)
res.send({
status: 200,
message: lang.__('general.success.created','Request')
})
} catch (err) {
switch (err.constructor) {
case DatabaseConnectionError: // Not connected to database
return res.sendStatus(500) // Internal server error
case UnauthorizedError:
return res.sendStatus(401) // Unauthorized
case default:
return res.status(400).send(err) // Generic error
}
}
}
Error classes:
class DatabaseConnectionError extends Error {}
class UnauthorizedError extends Error {}
Because you have that try/catch block inside your http handler method, anything that throws or rejects inside the Request.create method will be caught there. See https://repl.it/LtLo/3 for a more concise example of how errors thrown from async function or Promises doesn't need to be caught directly where they are first called from.