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()
Related
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
}
I am getting UnhandledPromiseRejection error even I wrapped the code in try catch block
I using await Prmomise.all together here
const express = require('express');
const app = express();
const port = 3003;
function testPromise(n) {
return new Promise(async (res, rej) => {
console.log(n);
if (n > 10) {
res(true);
} else {
setTimeout(() => {
rej(n);;
}, 1000)
}
});
}
function test2(n) {
return new Promise(async (res, rej) => {
console.log(n);
if (n > 10) {
res(true);
} else {
setTimeout(() => {
rej(n);;
}, 10000)
}
});
}
async function allCall(p) {
await Promise.all(p);
}
app.get('/', async (req, res) => {
try {
let a = [];
let b = [];
a.push(testPromise(1));
await test2(1);
a.push(testPromise(12));
// await Promise.all(a.map(m => m.then(() => { }).catch(err => { })));
await Promise.all(a);
res.send('Hello World!');
} catch (err) {
console.log('err');
console.log(err);
res.status(400).send('xxxxxxxxxx!')
}
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
I am not sure why it is throwing the error
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise
which was not handled with .catch().
Please explain why and how to resolve this ?
You are getting this error because 2 promises are getting rejected but try/catch only handles one. The second promise is rejected but not handled.
1st rejection: a.push(testPromise(1));
2nd rejection: await test2(1);
NOTE:
Both the promises are started parallel.
try/catch only works with async/await, if you write promise inside try/catch it'll not be handled by the catch block.
try {
Promise.reject("Something")
} catch (error) {
console.log('Not here');
}
// Unhandled promise rejection
Explanation:
When you push a promise to an array a.push(testPromise(1));, it starts execution and rejects after 1 second. It goes to catch.
same time the second promise also started await test2(1); because you are not waiting for the first promise to resolve/reject. It'll get rejected after 1 second and not handled by the catch block. it'll go to catch only if you use with await. If you want to handle first rejection you have to use .catch. after 10 seconds second promise get rejected and handled by catch block.
Solution
const r = await testPromise(1);
await test2(1);
Another solution:
async (req, res) => {
try {
// a.push(testPromise(1));
await test2(1);
let a = [testPromise(1), testPromise(12)];
await Promise.all(a);
console.log("done");
} catch (err) {
console.log("err");
console.log(err);
}
};
I'm tryng to upgrade this code for a better maintenance, this code uploads two images to a server, i know it's possible to get rid of those .catch, by applying async await functions, and try catch blocks, but it's pretty confusing for me, any help will be apreciated.
this._uploadService.makeFileRequest(Global.url + "/upload-image1/" + response.product._id, [], this.filesToUpload1, 'image')
.then((result: Product) => {
this.filesToUpload1 = null;
this._uploadService.makeFileRequest(Global.url + "/upload-image/" + response.product._id, [], this.filesToUpload, 'image')
.then((result: Product) => {
this.filesToUpload = null;
setTimeout( () => this._router.navigate(['/detail', this.saveProduct._id]), 800 );
})
.catch(err => {
console.log(err);
this._router.navigate(['/detail', this.saveProduct._id]);
})
})
.catch(err => {
console.log(err);
this._router.navigate(['/detail', this.saveProduct._id]);
})
I suggest using a pen and paper to draw a block diagram for the logic involved, i.e. which api gets called first, with what kind of data, then which api comes afterwards; also include any logical conditionals through branching.
After that, you should attempt to write something like
const aggregateFunction = async() => {
try {
const someResponse = await callFirstApi(); // return response
await callSecondApi(someResponse); // use the response of the first api for the second api
if (someConditional) {
await callThirdApi(); // response not returned (i.e. when not required)
}
} catch (error) { // catch all errors from all await if they're not within another try-catch
console.log(error);
}
}
This pattern should eliminate all then and catch blocks. If you need more specific error handling for calling say a specific api, wrap function call inside another try-catch block, but everything should still be within the outer try-catch so that all errors will be caught regardless.
this._uploadService.makeFileRequest = function(){
return new Promise(resolve => {
// do logic of file request
resolve(true);
})
}
var waitForTime = function() {
return new Promise(resolve => {
setTimeout( () => {
this._router.navigate(['/detail', this.saveProduct._id]),
resolve(true)
}, 800 );
})
}
var f = async function(){
try {
await this._uploadService.makeFileRequest(Global.url + "/upload-image1/" + response.product._id, [], this.filesToUpload1, 'image');
await this.fileToUpload1 = null;
await this._uploadService.makeFileRequest(Global.url + "/upload-image/" + response.product._id, [], this.filesToUpload, 'image')
await this.fileToUpload = null;
await waitForTime();
}
catch(e) {
// error logic
}
}
if (this.filesToUpload1 && this.filesToUpload) {
f()
}
this might be another cleaner approach with async,await and 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.
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;
}
}