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?
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'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
Introduction
I am new in the world of javascript promises and I need to understand how they work correctly...
Until know I have been doing this in my code:
const handleRefresh = () => {
setIsRefreshing(true);
fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
setIsRefreshing(false);
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
setIsRefreshing(false);
console.log(err)
);
};
const fetchUserData = async () => { <------ async function
const { firebase } = props;
const userId = firebase.getCurrentUser().uid;
const documentRef = firebase.getDatabase().collection("users").doc(userId);
// Fetch all the user information
return documentRef <--------- Returning the promise here
.get()
.then((doc) => {
if (doc.exists) {
// Get all user data
const data = doc.data();
console.log(data);
setUserData(data);
}
})
.catch((err) => {
throw err; <----------- Throwing error
});
};
I don't know if I am doing anti patterns... But basically I need to know if this is a good way and if I am doing this correctly.
Questions
Do I have to declare the fetchUserData function as async to return a promise?
Can I use the async await in the then/catch body?
Can I do this?
const handleRefresh = async () => {
setIsRefreshing(true);
await fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
console.log(err)
);
setIsRefreshing(false);
};
I would really appreciate if someone guides me. Thanks.
the words async and await are only syntactic sugar for then and catch.
This:
fetchUserData()
.then(data => return data )
.catch(error => return error)
is equivalent to:
async function getUserData() {
const userData = await fetchUserData()
return userData
}
Here you are returning anything (success or error). If you want to treat the error here, just put a try/catch clause.
async function getUserData() {
try {
return await fetchUserData()
} catch (e) {
return e.message
}
}
Note that you can only use the await clause within an async function.
1.
Function can return Promise without being declared as async as long as you don't await inside it,
2.
You should not use async-await inside then, simply return a Promise and it'll be resolved in the following then,
3.
When using async-await syntax, Promises are awaited in a declarative way as demonstrated below:
const handleRefresh = async () => {
try
{
const a = await getA()
// pass the result to another Promise
const b = await getB(a)
const c = await getC(b)
} catch (error)
{
handleError(error)
}
};
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;
}
}