I have a function that looks like this:
function x(req, res, next){
await doSomething().catch(error =>{
return res.send('error here'})
}
await doSomethingElse().catch(error =>{console.log(error)})
return res.send('success');
}
When doSomething() returns an error, I get "Cannot set headers after they are sent to the client", which I understand, but why doesn't the script terminate when I call the first res.send?
My guess is that it's similar to a callback in that the returned value is ignored, so it sends the response, but ignores the fact that the script should end there.
How can I fix this problem?
It gets very complicated to control flow when you are mixing .catch() and await. I would strongly suggest using only one model at a time. With await, you can do this:
async function x(req, res, next) {
try {
await doSomething();
await doSomethingElse();
res.send('success');
} catch(e) {
console.log(e);
res.send('error here'})
}
}
why doesn't the script terminate when I call the first res.send?
Because the return in that statement just returns back from the .catch() handler not from your main function and since you've done a .catch() and not thrown inside that catch handler or returned a rejected promise from the handler, then the parent promise because resolved and execution after the await continues just fine which then causes you to run into the second res.send() which causes the warning about headers already sent.
if you want to use .then() .catch() just try:
function x(req, res, next) {
doSomething()
.catch((error) => {
return res.send("error here");
})
.then(() => {
doSomethingElse()
.catch((error) => {
console.log(error);
})
.then(() => {
return res.send("success");
});
});
}
Related
function sendPushNotification(subscription, urlEncodedData){
try {
webpush.sendNotification(subscription, urlEncodedData);
} catch (err){
console.log('err');
console.log(err);
}
}
This doesn't catch the error, it is still considered an 'unhandledRejection'. Why?
If you're calling an async function, or a function that returns a Promise then you need to sequence things properly:
async function sendPushNotification(subscription, urlEncodedData){
try {
await webpush.sendNotification(subscription, urlEncodedData);
} catch (err){
console.log('err');
console.log(err);
}
}
Here await will capture any response. An error condition that will manifest as an exception.
Note that this makes sendPushNotification() return a Promise so you will have to treat it as asynchronous. This means the caller needs to await as well, and that may have impacts up the call chain depending on your sequencing requirements.
So I created a function in my node server which takes in a query string, runs it on my db, and returns the results. I then wanted to use my function asynchronously using async await throughout my routes instead of having nested query within, nested query, within nested query etc.
So here is the code:
const runQuery = queryString => {
console.log("...runQuery")
db.query(queryString, (error, results, fields) => {
if (error) {
console.log("runQuery: FAILURE");
return error;
}
else {
console.log("runQuery: SUCCESS");
return(results);
}
})
}
register.post("/", async (req, res) => {
console.log(req.body);
const results = await runQuery("select * from test1");
res.send(results);
})
The database should have 3 entries, but unfortunately, it returns nothing. Meaning results is an empty variable when it is sent, meaning JS never properly waits for it to capture the db results. How can I use my function asynchronously, and how is this even feasible?
It seems your function "runQuery" does not return a promise, in fact, it's not returning anything. You are using "return" in the callback of the db.query function, not the function "runQuery" itself.
Since runQuery is performing an asynchronous operation, the result ought to be resolved via a promise (which is what the "await" in your request handler is looking for).
I'm not exactly sure but it seems you are using MySql, and I could not find anything on the npm page of the mysql package regarding the query being promisified, so we'll promisify it ourselves:
const runQuery = (queryString) => new Promise((resolve,reject) => {
console.log("...runQuery")
db.query(queryString, (error, results, fields) => {
if (error) {
console.error("runQuery: FAILURE");
reject(error);
} else {
console.log("runQuery: SUCCESS");
resolve(results);
}
})
})
register.post("/", async (req, res) => {
console.log(req.body);
try{
const results = await runQuery("select * from test1");
res.send(results);
}catch(e){
console.error(`ERROR THROWN BY runQuery:`,e);
res.status(500).send({
message: e.message || "INTERNAL SERVER ERROR"
})
}
})
Note that if an error occurs, our promisified function will reject the error and it will NOT be stored in the "results" variable in our request handler. Instead, an error will be thrown which needs to be handled. Therefore, it is always a good practice to put any async/await calls inside a try/catch.
So I have an Express app that uses middleware to parse JSON POST requests and then populate a req.body object. Then I have a promise chain that validates the data against a schema using Joi, and then stores it in a database.
What I would like to do is check if an error was thrown after one of these processes, handle it appropriately by sending a status code, then COMPLETELY ABORT the promise chain. I feel like there should be some EXTREMELY CLEAN AND SIMPLE way to do this, (perhaps some sort of break statement?) but I can't find it anywhere. Here is my code. I left comments showing where I hope to abort the promise chain.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
You can't abort a promise chain in the middle. It's going to either call a .then() or a .catch() later in the chain (assuming there are both and assuming your promises resolve or reject).
Usually, the way you handle this is you put one .catch() at the end of the chain and it examines the type of error and takes appropriate action. You don't handle the error earlier in the chain. You let the last .catch() handle things.
Here's what I would suggest:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
This has several advantages:
Any error propagates to the last .catch() at the end of the chain where it is logged and an appropriate status is sent in just one place in the code.
Success is handled in just one place where that status is sent.
This is infinitely extensible to more links in the chain. If you have more operations that can have errors, they can "abort" the rest of the chain (except the last .catch() by just rejecting with an appropriate error object).
This is somewhat analogous to the design practice of not having lots of return value statements all over your function, but rather accumulating the result and then returning it at the end which some people consider a good practice for a complicated function.
When debugging you can set breakpoints in one .then() and one .catch() to see the final resolution of the promise chain since the whole chain goes through either the last .then() or the last .catch().
.catch returns a resolved Promise by default. You want a rejected Promsise. So, you should return a rejected promise from inside the .catch, so that future .thens won't execute:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
But note that this will result in a console warning:
Uncaught (in promise) ...
So it would be nice to add another .catch to the end, to suppress the error (as well as catch any other errors that come along):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
If you want to avoid all future .thens and future .catches, I suppose you could return a Promise that never resolves, though that doesn't really sound like a sign of a well-designed codebase:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
A cleaner solution for what you are trying to accomplish might be to use express-validation, which is a simple wrapper around joi that provides you with express middleware for validation of the body, params, query, headers and cookies of an express request based on your Joi schema.
That way, you could simply handle any Joi validation errors thrown by the middleware within your "generic" express error handler, with something like:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
If you don't want to use the express-validation package, you could write your own simple middleware that does more or less the same thing, as described here (see example here).
One strategy is to separate your error handling in subpromises which have their individual error handling. If you throw an error from them, you'll bypass the main promise chain.
Something like:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
You can skip the Promise.resolve().then() wrapping, but it's included for illustrative purposes of the general pattern of subdividing each task and its error handling.
async function(req, res) {
try {
const user = await userCtrl.getUser();
const userMaps = await mapsCtrl.findDetails(user.mapId);
res.send(userMaps);
} catch (error) {
//handle error
res.status(400).send(error)
}
}
// user controll
function getUser() {
return new Promise(function(resolve, reject) {
//data base read using mysql
req.app.get("mysqlConn").query(query, function(error, results, fields) {
if (error) {
reject(error);
}
resolve(results);
});
})
}
//maps controller function is also like above one.
This is the code handle part of an express get route. Sometimes the rejected code is not getting caught. I get the error returned from MySQL in 200 status code.
Yes you can write multiple awaits in a single try catch block. so your catch block will receive the error if any of the above await fails.
Refer this link for more information about async-await - https://javascript.info/async-await
reject() or resolve () doesn't mean the functions is terminated. So we have to explicitly return from the function to avoid further code execution. In your case put resolve in else block or just put return statement in the if block after the reject is called!
Refer this link for more information:- Do I need to return after early resolve/reject?
I hope this help :)
Regards.
I'm trying to get the hang of using Mongoose promises with the async/await functionality of Node.js. When my function printEmployees is called I want to save the list of employees which are queried by the orderEmployees function. While, the console.log statement inside orderEmployees returns the expected query, the console.log inside of printEmployees returns undefined, suggesting that I'm not returning the promise correctly.
I'm new to promises so entirely possible that I'm not correctly understanding the paradigm... any help is much appreciated.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
In order to make orderEmployees behave like async functions, you have to return the resulting promise. There are two rules to follow when using promises without async/await keywords:
A function is asynchronous if it returns a Promise
If you have a promise (for example returned by an async function) you must either call .then on it or return it.
When you are using async/await then you must await on promises you obtain.
This said you will notice that you do not return the promise generated inside orderEmployees. Easy to fix, but its also easy to rewrite that function to async too.
orderEmployees: (companyID) => {
return User.find({company:companyID}) // Notice the return here
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
or
orderEmployees: async(companyID) => {
try {
const employees = await User.find({company:companyID}).exec();
console.log(employees);
return employees;
} catch (err) {
return 'error occured';
}
},
PS: the error handling is somewhat flawed here. We usually do not handle errors by returning an error string from a function. It is better to have the error propagate in this case, and handle it from some top-level, UI code.
You need to return your Promise.
Currently, you are awaiting on a function that returns undefined.
await only actually "waits" for the value if it's used with a Promise.
Always keep in mind that you can only await Promises or async functions, which implicitly return a Promise1.
orderEmployees: (companyID) => {
return User.find({ company:companyID }).exec()
}
Also really important, you should throw instead of return in your .catch handler. Returning from within a .catch handler will cause the promise chain to trigger it's .then instead of it's .catch thus breaking the error handling chain.
Better yet, don't include .catch at all and let the the actual error bubble up the promise chain, instead of overriding it with your own non-descriptive 'error occured' message.
Error conditions should throw the error, not return it.
1 You can also await non-Promises, but only for values that are evaluated synchronously.
You are not returning a Promise from orderEmployees.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
return User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
You need to return a Promise from orderEmployees
orderEmployees: companyId => User.find({ companyId }).exec()
If you want to do some error handling or pre-processing before you return then you can keep your code as is but just remember to return the result (promises are chainable).
if you're going to use async/await then it works like this.
await in front of the function that returns a promise.
async in front of the wrapping function.
wrap the function body inside try/catch block.
Please have a look on this function, it is a middleware
before i execute a specific route in express.
const validateUserInDB = async (req, res, next) => {
try {
const user = await UserModel.findById(req.user._id);
if (!user) return res.status(401).json({ message: "Unauthorized." });
req.user = user;
return next();
} catch (error) {
return res.status(500).json({ message: "Internal server error." })
}
}
The code after await is waiting the promise to be resolved.
Catch block catches any error happened inside the try block even if the error that is triggered by catch method comes from awaiting promise.