alternative to nested try catch in javascript - javascript

I am working on MEAN STACK web application and in nodejs I want to use async await with try catch block. In following example I want to make code in straight instead of using nested multiple try and catch. Here I need to pass different type of custom errors to front-end if any error arise.
EX :
public async create(req: Request, res: Response) {
...
...
...
try {
...
...
...
let userResult = await userModel.addBasickInfo(userData);
...
...
...
try {
...
...
...
let userAddressResult = await userProfileModel.addAddress(addressData);
...
...
...
try {
...
...
...
let userProfileResult = await userAddressModel.addProfile(profileData);
...
...
...
return (<any>res).sendResponse(data, 'Information saved successfully');
} catch (err) {
return (<any>res).sendError(err.error ? err : new ErrorException('BadRequestError', "Error while adding user profile information"));
}
} catch (err) {
return (<any>res).sendError(new ErrorException('BadRequestError', 'Error while adding user address'));
}
} catch (err) {
return (<any>res).sendError(err.error ? err : new ErrorException('BadRequestError', 'Error while adding user information'));
}
}
Please help me to improve this code.

You can try this pattern instead of nesting the try-catch block.
public async create(req: Request, res: Response) {
try {
let userResult = await userModel.addBasickInfo(userData);
} catch (err) {
return (<any>res).sendError(err.error ? err : new ErrorException('BadRequestError', 'Error while adding user information'));
}
try {
let userAddressResult = await userProfileModel.addAddress(addressData);
} catch (err) {
return (<any>res).sendError(new ErrorException('BadRequestError', 'Error while adding user address'));
}
try {
let userProfileResult = await userAddressModel.addProfile(profileData);
return (<any>res).sendResponse(data, 'Information saved successfully');
} catch (err) {
return (<any>res).sendError(err.error ? err : new ErrorException('BadRequestError', "Error while adding user profile information"));
}
}

You will need to wrap your async code with a small utility function. This function will format the resolving and rejecting arguments to an array of two elements.
/**
* #param { Promise } promise
* #param { Object } improved - If you need to enhance the error.
* #return { Promise }
*/
export function utility(promise, improved){
return promise
.then((data) => [null, data])
.catch((err) => {
if (improved) {
Object.assign(err, improved);
}
return [err]; // which is same as [err, undefined];
});
}
This function is returning an array of two elements:
On the then callback (if the Promise resolved): it returns null and
the data as there are no errors.
On the catch callback (if the Promise rejected): it returns the err
that can be extended and undefined as the second element as there is
no data.
You can now take your original try/catch block and update it that way. What is done here is simply wrapping the someAsyncData Promise by our utility function.
const [error, result] = await utility(someAsyncData());
if(error){
// log something and return ?
}
const [error2, result2] = await utility(someAsyncData2());
if(error2){
// do something else
} else {
// Here we are sure that result2 is defined and a valid value
}

Related

Return value from externally called fs.Readfile() in node

I am trying to get a value returned from a function where I read and write a file using fs.readFile/writeFile in Node.
In my main server.js file, a request comes in and I then want to send an email from another file called sendEmail.js:
const fs = require('fs')
const sendMail = require('./sendEmail')
async function sendAnEmail() {
let resultOfSend = await sendMail.sendEmail()
resultOfSend.then((result)=>{
// return the result
}
}
sendAnEmail();
In sendEmail I first read a file to get the email to send to,
then write to a second file
then, if all is good, I send an email (from a separate function):
async function sendEmail() {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
throw error
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
throw error
}
else {
return generateEmail(users)
.then((info) => {
return info
})
.catch(console.log('err'))
}
})
}
})
}
async function generateEmail(user) {
let msgText = 'hello world'
// Set the mail options
const mailOptions = {
...
}
// Send the mail
let info = await transporter.sendMail(mailOptions)
return info
}
module.exports = {sendEmail}
What I can't get is a value for the resultOfSend variable. Keeps coming back undefined, I think because the promise hasn't yet been fulfilled.
How do I get a value to return from the sendEmail function back to the server.js file where it's called from?
You're using await and async in sendEmail but not returning any Promise (So the sendEmail function doesn't return anything and this is why you get undefined).
Nevertheless, on the response you're trying to call .then() even though you used await.
So you should:
return Promise in the sendEmail function.
decide how you want to handle it, if you use async-await then dont use .then() and just analyze the variable and vice versa.
generateEmail() function should also return Promise.
For example:
async function sendEmail() {
return new Promise((resolve, reject) => {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
reject()
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
reject()
}
else {
generateEmail(users)
.then((info) => {
resolve(info)
})
.catch(
console.log('err')
reject()
)
}
})
}
})
})
}

Try/Catch in mulitple awaits in express app

I am making a few database calls and I am using async/await and try/catch for error handling. I am struggling if I should have all of the db calls in one try/catch, or have multiple try/catch` blocks for each call.
I also have a few calls in callback fncs, I am not confident those calls will be caught in my catch block if I only have one try/catch. With that in mind, those calls have their own try catch block. Here is a working example:
exports.syncStaff = async function (req, res, next) {
// ShiftTask && Shift is a model from mongoose
try {
// DB CALL #1 --> Inside of Try/Catch Block
const shift = await Shift.findById(req.params.id);
// DB CALL #2 + 3 --> Two calls run in parallel --> Inside of Try/Catch Block
const [shiftTasks, shiftType] = await Promise.all([
ShiftTask.find({ group: shift.id }),
mongoose.model('ShiftType').findById(shift.type).populate('tasks').select('tasks')
]);
await Promise.all(shiftTasks.filter(st => !shiftType.workshops.find(type => type.id.toString() === st.task.toString() || st.status !== 'pending')).map(task => {
// DB CALL #4 --> Separate Try/Catch Block, is this needed?
try {
return ShiftTask.remove({ _id: task.id });
} catch (error) {
console.error(error);
next(error);
}
}));
await Promise.all(shiftType.workshops.filter(type => !shiftTasks.find(task => task.shift.toString() === type.id.toString())).map(type => {
try {
// DB CALL #5 -- Separate Try/Catch Block, is this needed?
return ShiftTask.create({
group: shift.id,
eventType: type.id
});
} catch (error) {
console.error(error);
next(error);
}
}));
return await res.status(201).json('still to be decided');
} catch (error) {
console.error(error);
next(error);
}
};
Are the try/catch blocks in db calls #4 and #5 necessary?
I don't think external try catch blocks are needed. If an error is thrown from somewhere, it can be caught from the block in the public container. I made an example like this. You can test the 2nd case specified in the code from the google chrome console.
let compPromise = new Promise(function(resolve, reject) {
resolve('Complete');
});
let errPromise = new Promise(function(resolve, reject) {
reject(new Error("Whoops promise reject!"))
});
let exec = async () => {
try {
let res1 = await compPromise;
console.log('res1', res1);
let [res2,res3] = await Promise.all([
compPromise,
compPromise
])
console.log('res2', res2);
console.log('res3', res3);
// In this case, reject will return from the promise and will catch it in the catch block.
await Promise.all([10, 20, 30].map((x) => x === 30 ? errPromise : compPromise))
// In this case, the parameter was deliberately sent as undefined and will still be caught in the catch block.
await Promise.all([undefined, 'Johnny', 'Alison'].map((x) => x.trim().includes("n") ? errPromise : compPromise))
} catch(err) {
console.log(err.toString());
}
}
exec()

Changing script from request to axios - log pending promise

I'd like some help please as I'm quite new in node.js and working with node packages.
I'm having the following script which makes a GET http request running on node using request which is deprecated now
const foo = (bar, callback) => {
const url = 'https://some.api.com?key=abc123';
request({url: url, json: true}, (error, response) => {
if (error) {
callback('Oops, there is an error!', undefined);
} else if(response.body.foobarArray.length === 0) {
callback('No data found', undefined);
} else {
callback(undefined, {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
})
}
});
}
console.log(foo('Hello')); // this logs {foobar1: 'Hello', foobar2: 'World'}
I'm trying to rewrite it using axios instead, so this is my code
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
const response = await axios.get(url);
if (response.body.foobarArray.length === 0) {
return 'No data found';
} else {
return {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
};
}
} catch (error) {
return 'Ooops! Something went wrong :(';
}
};
console.log(foo('Hello')); // This logs `Promise { <pending> }`
I'm not sure what I'm doing wrong here as I'm not very familiar how promises work exactly, but how can I fix this?
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
return await axios.get(url).then(response => {
return new Promise((resolve, reject) => {
if (response.body.foobarArray.length === 0) {
return reject('No data found');
} else {
return resolve({
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
});
}
})
}).catch(err => {
return Promise.reject(err);
});
} catch (error) {
// return 'Ooops! Something went wrong :(';
return Promise.reject(`an error occurred : ${error}`);
}
};
foo('hello').then(result => {
console.log(result);
}).catch(err => {
console.log(`error ! : ${err}`);
});
async functions returns a promise. async functions use an implicit Promise to return its result. Even if you don't return a promise explicitly async function makes sure that your code is passed through a promise
as you are using axios asynchronous , it's response is a promise which must be handled inside .then().catch() functions .
if no error occurs you can access the response inside your .then() , else you will have access to your error on .catch()
inside your .then() you can now do what you want with data , returning a new Promise , using resolve() for success and reject() for failure .
You have 2 options here:
Option 1
Any async function returns a Promise (behind the scenes) so:
foo('Hello').then(console.log).error(console.error);
Option 2
You need to await for the result of foo function but, at the moment, you can't use await out of function scope level. So:
async function main() {
try {
const result = await foo('Hello');
console.log(result);
} catch (err) {
console.error(err);
}
}
main();
In future Node.js releases, using await at global scope will be allowed.

How to use .catch inside async-await?

I have a promise which looks like this.
const updateTheme = async rawTheme => {
try {
const existData = await Theme.findOne({ themename: rawTheme.themename });
if (!existData) {
return Promise.reject(Error(`Theme ${rawTheme.themename} not found.`));
}
Object.assign(existData, rawTheme);
const result = await existData.save();
return result;
} catch (err) {
return Promise.reject(err);
}
};
This function updates a theme in mongoDB and if it doesn't find any theme, then it returns Promise.reject.
I want to create a function which uses updateTheme. If it doesn't find a theme, it creates a new theme. The function is like this.
const updateTheme = async dbdata=> {
try {
let data = await dbServices.updateTheme(dbdata);
return Promise.resolve(data);
// Modify this function to catch an error from updateTheme and create a new theme
} catch (error) {
return Promise.reject(error);
}
}
I want something like this?
await dbServices.updateTheme(dbdata)
.then(
// resolve logic
).catch(
// check reject error and write create new theme logic
)
But if we use async-await, any error will go directly into try...catch block. So my question is, how to catch the error in .then.catch block?

Return or skip from catch in async function

I have a async function whose output (resolve/reject) I translate with then/catch.
I want to end the outer function with return but I can only return within catch somehow.
How can I skip/quit/return on the outside of catch or await?
await this.authService.auth(this.oldUser).then( resolve => {
//went in authService and resolve can be used
}).catch( reject => {
//in catch as authService rejected and want to return to outer
//function
return;
})
//Second attempt should only be done if first attempt "resolved"
await this.authService.auth(this.newUser).then( resolve => {
}).catch( reject => {
return;
})
You can have the .then and .catch return something meaningful that distinguishes them, and then test that distinguishing factor. For example:
const result = await this.authService.auth(this.oldUser).then((authorizedUser) => {
// do stuff with authorizedUser
return authorizedUser;
}).catch((err) => {
// handle errors, if needed
return { err };
});
if (result.err) {
// there was en error, return early:
return;
}
// rest of the code that depends on the first request being successful goes here
await this.authService.auth(this.newUser).then(...)
Note that if you're using await, it might make a bit more sense to use try/catch rather than .thens and awaits:
try {
const authorizedUser = await this.authService.auth(this.oldUser)
// do stuff with authorizedUser
// rest of the code that depends on the first request being successful goes here
const newAuthorizedUser = await this.authService.auth(this.newUser);
// do stuff with newAuthorizedUser
} catch(err) {
// handle errors, if needed
return;
}
private async authenticate(oldUser: User) {
try {
await this.authService.auth(this.oldUser).toPromise();
return;
} catch (reject) {
return;
}
}

Categories