Testing console.log in catch block - javascript

I have the following code:
loggerManager.js:
export default log(message) {
try {
axios.post(...).catch(error) { console.log(error); }
}catch(error) {
console.log(error);
}
}
loggerManager.test.js:
test('', () => {
jest.spyOn(global.console, 'log');
jest.mock('axios');
axios.post = jest.fn(() => Promise.reject('fail'));
log('message');
expect(console.log).toBeCalledWith('fail'); // The result said it has not been called even once
});
Where am I going wrong?

Two issues:
A rejection handler converts rejection into fulfillment if it doesn't throw or return a promise that is rejected
try/catch doesn't catch promise rejections except when the promise is consumed via await within the try block
So assuming the normal case, if your axios.post's promise rejects, the rejection handler runs but the catch block does not.
In this code:
export default log(message) {
try {
axios.post(...).catch(error) { console.log(error); }
}catch(error) {
console.log(error);
}
}
Execution will only go into the catch block at the end if:
axios is an undeclared identifier; or
axios doesn't have a post property; or
axios.post isn't a function; or
axios.post throws when called; or
axios.post doesn't return an object with a catch property; or
The catch property of the object axios.post returns isn't a function; or
The catch method of the object axios.post returns throws when called
It will not be entered if axios.post returns a promise that is rejected.
You may have wanted this:
export default async log(message) {
// −−−−−−−−−−−−^^^^^
try {
await axios.post(...);
// −^^^^^
}catch(error) {
console.log(error);
}
}
That way, the catch block will be entered if there was a problem calling axios.post or if axios.post returns a promise that is rejected.

Related

Why isn't react component catching axios error

I have the following code on python using flask
#bp.route("/test", methods=["GET"])
def test():
throw_error = True
if throw_error :
return jsonify(message="Throwing an error"), 405
return jsonify(message="Test message"), 200
on React I have a context setup with the following function
function testRequest(){
const response = axios.get('/api/test')
console.log(response)
}
I'm calling this function on a button click in another component by
async function handleButtonClick(e){
e.preventDefault();
try{
await testRequest();
}catch(error) { // DOESN'T EXECUTE??
console.error("Error occured")
setError("Error occurred in test method")
}
}
Why isn't the try catch, catching the 405 error?
You can only usefully await a promise. testRequest doesn't return a promise.
It triggers axios.get, assigns the promise to response, logs it, then returns undefined.
The try/catch doesn't touch the promise at all.
You could fix that with:
function testRequest(){
const response_promise = axios.get('/api/test')
console.log(response_promise)
return response_promise;
}
So the promise is being awaited inside the try/catch.
With below modification to your function, you will be able to catch error.
async function testRequest() {
const response = await axios.get('http://localhost:1337/sample/test');
return response;
}
async function handleButtonClick(e:any) {
e.preventDefault();
try {
await testRequest();
} catch (error) { // DOESN'T EXECUTE??
console.error("Error occured")
console.log(error.mes);
}
}

Handling Promise rejection with catch while using await

I am using await to make the code cleaner, but I am not sure whether I am handling exceptions correctly.
An example while using azure-devops-node-api;
const foo = async() => {
return new Promise((resolve, reject) => {
...
...
const teams = await coreApiObject.getTeams(currProject.id)
.catch(err => { reject(err) return })
...
...
})
}
In this code I am assuming, if there is a problem with promise call, foo() is going to return reject.
async functions always return a promise, so you don't need to explicitly create one yourself. Any non-promise value returned from an async function is implicitly wrapped in a promise.
Inside the foo function, you just need to await the call coreApiObject.getTeams(...) and to catch and handle any error, use the try-catch block.
Your code can be simplified as shown below:
const foo = async() => {
try {
const teams = await coreApiObject.getTeams(currProject.id);
return teams;
} catch (e) {
// handle error
}
}
If you want to the calling code to handle the error, then you can use one of the following options:
Remove the try-catch block and just return the result of coreApiObject.getTeams(...).
const foo = async() => {
return coreApiObject.getTeams(currProject.id);
}
Removing the try-catch block and just returning the call to coreApiObject.getTeams(...) will allow the calling code to handle the error because the promise returned by the foo function will get resolved to the promise returned by coreApiObject.getTeams(...); this means that the fate of the promise returned by the foo function will depend on whatever happens to the promise returned by coreApiObject.getTeams(...).
If the promise returned by coreApiObject.getTeams(...) is rejected, promise returned by the foo function will also be rejected and hence the calling code will have a change to catch the promise rejection and handle it.
Throw the error from the catch block.
const foo = async() => {
try {
const teams = await coreApiObject.getTeams(currProject.id);
return teams;
} catch (error) {
// throw the error
throw error;
}
}
Other option is to throw the error from the catch block to make sure that the promise returned by the async function is rejected.
If you don't throw the error or return a promise or a thenable that is rejected, returning any other value from the catch block will fulfil the promise returned by the async function with whatever value is returned inside the catch block.

Using then() on a failed promise?

I have a store method...
async store() {
try {
return await axios.post('/upload', data);
} catch (error) {
}
},
Called by:
store().then(()=>{ console.log('ok'); }, ()=>{ console.log('not ok'); });
But when the store method fails and an error is caught, the first method in then is always called, how can I get the failed not ok method to be called?
You need to throw the error caught in the catch block of the store function
async store() {
try {
return await axios.post('/upload', data);
} catch (error) {
throw error;
}
}
You could also skip catching the error in the store function and simply catch it when store function is called. To do this, you just need to return the result of axios.post(...).
async store() {
return axios.post('/upload', data);
}
(Note that, without the try-catch block, you don't need an await before axios.post(...) because the promise returned by the store function will be resolved to the promise returned by axios.post(...). This means that if the promise returned by axios.post(...) is fulfilled, promise returned by the store function will also fulfil with the same fulfilment value with which the promise returned by axios.post(...) fulfilled.)
It is uncommon to pass second argument to then function. Instead, you should chain a .catch() block to catch the error.
store()
.then(() => console.log('ok'))
.catch(() => console.log('not ok'));

How to use single catch function for nested async await?

I have this code:
on('connection', async(socket) => {
try {
const promises = members.map(async(member) => {
try {
const users = await User.find({})
const _promises = users.map(async() => {
try {
//some code here
} catch (err) {
console.log(err)
}
})
await Promise.all(_promises)
} catch (err) {
console.log(err)
}
})
await Promise.all(promises)
} catch (err) {
console.log(err)
throw err
}
})
As you can see I have a try catch for each nested async function. Would it be possible to tweak the code to use only a single catch, or to simplify things somehow?
You can have just a single await at the top level of the handler. If the Promises spawned inside it all chain together to a Promise which is awaited at the top level (like in your example), that top await will catch errors thrown anywhere inside.
But, your catch section should not throw another error unless .on handles Promise rejections as well, otherwise you'll get an unhandled Promise rejection:
on('connection', async(socket) => {
try {
const promises = members.map(async(member) => {
const users = await User.find({})
const _promises = users.map(async() => {
//some code here which may throw
});
await Promise.all(_promises);
});
await Promise.all(promises);
} catch (err) {
console.log(err);
}
})
If await User.find throws, then the promises array will contain a Promise which rejects, which mean that the top await Promise.all(promises); will throw and be caught
If something inside users.map throws, _promises will contain a Promise which rejects, so await Promise.all(_promises); will throw. But that's inside the const promises = members.map callback, so that will result in promises containing a rejected Promise too - so it will be caught by the await Promise.all(promises); as well.
If a function you call throws an error, the error will fall back to the nearest catch block the function call is enclosed in.
try {
throw "an error";
}
catch(e) {
console.log(e); //output: "an error"
}
Now consider this
try {
try {
throw "an error";
}
catch(e) {
console.log(e); //output: "an error"
}
}
catch(e) {
console.log(e); //This line is not going to be executed
}
This mechanism enables attachment of more error information to the generated error in each level. Imagine your error is an object and each nested catch bock attaching its own information to the error object an pass it along by throwing again.
Look at the following code:
try {
try {
throw {internalError: 101};
}
catch(e) {
//Attach more info and throw again
e.additionalInfo = 'Disconnected when gathering information';
throw e;
}
}
catch(e) {
e.message = 'Cannot get information'
console.log(e); //Final error with lot of information
}

Async function Uncaught (in promise) undefined error

I'm trying to do some validations before creating/updating an entry as shown below:
async save(){
return new Promise((resolve, reject)=>{
if(!this.isCampaignValid){
this.handleError()
reject()
}
else{
this.$store
.dispatch('updateCampaign')
.then((res)=>{
resolve()
this.showNotification(res.message, 'success')
})
.catch(error=>{
this.showNotification(error.message, 'error')
reject()
})
}
})
},
the isCampaignValid is a computed value which computes the validity.
If the campaign is not valid, then I'm getting an error in the console as below:
Uncaught (in promise) undefined
The this.handleError() function works too. How can handle this promise error situation?
Just in case handleError() throws, try:
if (!this.isCampaignValid) {
try {
this.handleError()
} catch (e) {
console.error(e);
}
reject()
}
First of all, you don't need to return a promise in an async function. It implicitly returns one, resolving with the value returned by the function or rejecting with the error object if the function throws. Although you could return a promise and JS unpacks it for you, it's unneeded code.
That said, because async returns a promise, you'll have to catch that promise too. Since your first conditional block just throws an error but doesn't catch it, the promise returned by save will reject. You need to handle that rejection.
Here's a simplified version of your code to see where it's happening.
async save(){
if(!this.isCampaignValid){
this.handleError()
// Throwing an error in an async function is equivalent to a reject.
throw new Error('Campaign is not valid') // Here
}
else{
try {
const res = await this.$store.dispatch('updateCampaign')
this.showNotification(res.message, 'success')
} catch (e) {
this.showNotification(error.message, 'error')
}
}
},
// When you call save, catch the error
yourObject.save()
.then(() => {...})
.catch(() => {...})
// If your call is in an async function, you can try-catch as well
try {
await yourObject.save()
} catch(e) {
// It failed.
}

Categories