Javascript uncaugh error in promise even with a catch - javascript

With Javascript, I am trying to make a call to an external API when a form is submited.
I am using promises in order to set follow up actions once the call is done and to catch eventual errors.
This is where my problem is, even if I think I am catching my errors correctly, the console throws an
Uncaught (in promise) error : [The error I throw]
I do not understand why.
Here is a minimal version of my code which would reproduce the error when the refreshToken is expired :
try {
functionGeneratingTheError();
} catch (error) {
doSomethingElse();
}
function functionGeneratingTheError() {
var getTokenCallPayload = {
"client_id" : clientId,
"client_secret" : clientSecret,
"refresh_token" : refreshToken,
"grant_type" : "refresh_token"
};
var getTokenCallOptions = {
"method" : "POST",
"body" : JSON.stringify(getTokenCallPayload),
"muteHttpExceptions" : false
};
fetch(tokenURL, getTokenCallOptions)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error");
}
})
.then(data => {
doSomething();
})
.then(response=> {
doSomethingAgain();
})
.catch(error => {
throw error;
});
}
If I understand correctly, when the fetch is a bad request, it should throw the error "Error" which should then be caught in the first catch and run the doSomethingElse() function.
However, instead of doing that, I get this error in the browser console "Uncaught (in promise) Error: Error"
What am I doing wrong ?
I have tried including the fetch in a try{}catch(){} but it doesn't change anything.
I also tried not throwing the error and directly call my doSomethingElse() function, but then the following .then fails because data is undefined.

Change your functionGeneratingTheError function to return the chained promise like below:
function functionGeneratingTheError() {
var getTokenCallPayload = {
"client_id" : clientId,
"client_secret" : clientSecret,
"refresh_token" : refreshToken,
"grant_type" : "refresh_token"
};
var getTokenCallOptions = {
"method" : "POST",
"body" : JSON.stringify(getTokenCallPayload),
"muteHttpExceptions" : false
};
return
fetch(tokenURL, getTokenCallOptions)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error");
}
})
.then(data => {
doSomething();
})
.then(response=> {
doSomethingAgain();
})
.catch(error => {
throw error;
});
}
And then await it in your calling code by wrapping the calling code inside an async self invoking function like so:
(async function() {
try {
await functionGeneratingTheError();
} catch (error) {
doSomethingElse();
}
})();
You can read more about async/await here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Related

How to use Jest .mockImplementation to both throw and error and return status

I'm trying to test the catch block of this try/catch.
try {
const response = await request.get({
// ... required params here
});
return response.data;
} catch (error) {
myFunction({
statusCode: error.response.status,
});
throw error;
}
My aim is to test the call of myFunction inside the catch. I want to confirm that the function was called with the correct parameters of { statusCode: 400 } for example.
I also want to test that an error was thrown.
I've written a mockImplementation of request.get in an attempt to get this error response, but it does not seem to call into my catch block.
it("should throw an error and call myFunction with the status code", async () => {
await request.get.mockImplementation(() => {
throw new Error();
// how do I add return of statusCode here?
});
expect(addToNewRelic).toHaveBeenCalledWith(
expect.objectContaining({
productAPIRequestStatusCode: 404,
})
);
});

How to use a conditional validation after Axios call in Vue app

I have a Vue application where I make a POST request to my backend. I am now trying to call a validation method after the response from my backend returned back an error to my frontend. But for some reason my code is not executed:
UPDATED QUESTION CODE:
validateFormInput(){
this.$refs.form.validate()
},
saveSelectionVoter() {
var pageURL = window.location.href;
var lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
this.votersSelectArray.voterAvailableTimes = [...this.votersSelectArray.voterAvailableTimes, ...this.selected]
console.log(JSON.stringify( this.votersSelectArray))
axios.post("http://localhost:8080/api/votercontroller/",
this.votersSelectArray,
{
params: {
meetingName: lastURLSegment,
}
},
).then(function(response){
})
.catch(function (error){
this.validateFormInput()
console.log(error)
})
this.selected = []
},
This causes a new error:
TypeError: Cannot read property 'validateFormInput' of undefined
always have a catch to see the error return
axios return you a promise so it captures the error if there is any
axios.post('url')
.then((res) => {
// do somthing
}).catch(err){
console.log(err)
}
You can either use the callback method to catch the response/error or use the Promise way, which is my favorite because of scope and readability.
You start by declaring your function with async
async saveSelectionVoter() {
Then you use a try/catch block to handle the response/error:
try{
const response = await axios.post(url, params)
// handle response here
} catch (error) {
// handle error here
}

Converting jQuery ajax to fetch

I have this piece of code that calls a function getTableData and expects a Promise in return.
function populateTableRows(url) {
successCallback = () => { ... };
errorCallback = () => { ... };
getTableData(url, successCallback, errorCallback).then(tableData => {
// do stuff with tableData
}
}
This is used in many places across my codebase, and I'm looking to keep the behavior the same as I move away from using jQuery's ajax (and jQuery in general)
In getTableData, I'm currently using $.ajax like so
function getTableData(url, successCallback, errorCallback) {
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
const ajaxOptions = {
type: 'POST',
url: url,
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
data: { // some data }
};
return $.ajax(ajaxOptions).done(successCallback).fail(errorCallback);
}
This currently returns a Promise for successful requests. For bad requests where fail is invoked, it doesn't appear that a Promise is returned and the then doesn't run in the calling function (which is okay in this case).
When converting the request over to use fetch, I have something like this
function getTableData(url, successCallback, errorCallback) {
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
credentials: 'include',
body: { // some data }
})
.then(response => {
let json = response.json();
if (response.status >= 200 && response.status < 300) {
successCallback(json);
return json;
} else {
return json.then(error => {throw error;});
}
}).catch((error) => {
errorCallback(error);
return
});
Successful requests appear to be behaving similarly to the ajax code that I currently have, but now the then callback is running for bad requests which is causing errors in my code.
Is there a way with fetch to mimic the fail behavior of jQuery where the Promise is seemingly aborted for bad requests? I'm fairly new to using Promises and after some experimentation/searching I haven't been able to come up with a solution.
When you .catch() in a chain of promises, it means you already handled the error, and subsequent .then() calls continue successfully.
For example:
apiCall()
.catch((error) => {
console.log(error);
return true; // error handled, returning true here means the promise chain can continue
})
.then(() => {
console.log('still executing if the API call fails');
});
What you want, in your case, is when you handle the error with the callback, to continue to throw it so the promise chain is broken. The chain then further needs a new .catch() block to handle the new error.
apiCall()
.catch((error) => {
console.log(error); // "handled", but we're still not done
throw error; // instead of returning true, we throw the error further
// 👆 this can also be written as `return Promise.reject(error);`
})
.then(() => {
console.log('not executing anymore if the API call fails');
})
.catch((error) => {
// handle the same error we have thrown from the previous catch block
return true; // not throwing anymore, so error is handled
})
.then(() => {
console.log('always executing, since we returned true in the last catch block');
});
By the way, what you return from one then/catch block, the following one will get it as a param.
apiCall()
.then((response) => {
/* do something with response */;
return 1;
})
.catch((error) => { return 'a'; })
.then((x) => console.log(x)) // x is 'a' if there's an error in the API call, or `1` otherwise
In your .catch you implicitly return undefined and thus "handle" the error. The result is a new Promise that fulfills to undefined.
.catch((error) => {
errorCallback(error);
return Promise.reject();
});
should be enough to keep the returned Promise rejecting.
Or you assign the intermediate Promise to a var and return that, and not the result to the fail handling:
var reqPromise = fetch(url, {
// ...
})
.then(response => {
// ...
return json.then(error => {throw error;});
});
reqPromise.catch((error) => {
errorCallback(error);
return
});
return reqPromise;

Why catch invoked with success promise?

I have maybe weird things happen I send a request to an Endpoint "/Login" and it's given me the response well!
and I do my stuff, but for some reason, I see a warning that says,
Unhandled Promise Rejection / typeError: undefined is not an object
(evaluating 'error.response.data')
If anybody has an explanation for it?
Code snippet
signIn = data => {
this.setState({loading: true});
API.post('/login', data)
.then(response => {
let {
data: {
data: {
response: {token},
},
},
} = response;
this.setState({loading: false});
reactotron.log('response', response);
reactotron.log(token);
deviceStorage.saveKey('id_token', token);
})
.catch(error => {
alert('catched!!'); // it's appear :)
this.setState({error: error.response.data, loading: false});
reactotron.error(error.response.data);
});
};
=
The wrong was here in this function ~_~ !
why get me an error?
_deviceStorage.default.saveKey is not a function
import { AsyncStorage } from '#react-native-community/async-storage';
import reactotron from 'reactotron-react-native';
const deviceStorage = {
// our AsyncStorage functions will go here :)
saveItem= async(key, value)=>{
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
reactotron.log('AsyncStorage Error: ' + error.message);
}
}
};
export default deviceStorage;
error is a string representation of the error message( rejection message). But you are treating it as if it's an object when you say error.response.data
.catch(error => {
alert('catched!!'); // it's appear :)
this.setState({error: error, loading: false});
reactotron.error(error.response.data);
});
The catch could be invoked if an error is encountered in the then block. Check your then block for any possible errors. That might be the reason why catch is invoked even when the promise returned success.
const promise1 = new Promise(function(resolve, reject) {
resolve("resp");
});
promise1.then(function(resp) {
console.log(resp);
throw 'error'
}).catch(function(error) {
console.error(error);
});

this await throwing unexpected token error

I have a simple async function. It just sends a request and returns the data:
export const updatePanorama = async ({ commit }, payload) => {
const urlEnd = '/v1/pano/update'
const type = 'post'
const resp = await api.asyncRequest(urlEnd, type, payload)
commit('SET_PANORAMA', resp.data)
return resp
}
And this is how I'm using the function:
handleUpdatePanorama (panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
The problem is, the code after catch runs if there's an error inside then. But this way I don't know whether the catch error is an request error or and error triggered by the code inside then.
I'm trying try and catch to solve that problem:
handleUpdatePanorama (panorama) {
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
} catch (err) {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
However, I get an unexpected token error in this line: await this.updatePanorama(payload)
What am I doing wrong?
The problem is, the code after catch runs if there's an error inside then
The solution for that is to not use catch, but the second then parameter. Have a look at the difference between .then(…).catch(…) and .then(…, …) for details.
I'm trying try and catch to solve that problem
That won't work, the catch clause will still be called if there's an exception thrown by setIsLoading or handleAlert.
I get an unexpected token error. What am I doing wrong?
You have not declared the handleUpdatePanorama method as async.
To mitigate the issues and fix the syntax, you could write
async handleUpdatePanorama (panorama) {
var result
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
result = ['updateSuccess', 'success']
} catch (err) {
result = ['updateError', 'danger']
} finally {
this.setIsLoading(false)
}
this.handleAlert(...result)
},
If you need to handle errors specifically from updatePanorama, use the second argument to .then(onSuccess, onError)
handleUpdatePanorama(panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}, err => {
// handle error from updatePanorama
// you can throw err if you also want to handle the error in .catch()
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
}
note: if you return (or have no return statement) from the error handler, any subsequent .then(onSuccess will execute, if you throw an error (or return Promise.reject() for example, then the .catch() code will also run

Categories

Resources