Axios handle error using interceptor (js promises) - promise

I am using an interceptor in axios to check for errors.
service.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config
if (!error.response) {
// No network connectivity
}
}
)
My request looks like this
service.post('endpoint').then(response => {
// Successful request
}).catch(error => {
// Handle error
})
In case there is any error such as an unsuccessful status code (e. g. 400), I wan't to handle the error in the catch part of the second code example. However, if there is a network issue, I wan't to handle the error in the first code example. In that case neither then, nor catch of the second code example should be called. How can I achieve this?

When you have a promise chain already in place you can't stop the flow:
const axios = require('axios')
axios.interceptors.response.use(
response => response,
manageErrorConnection
)
// here you have created a promise chain that can't be changed:
axios.get('http://localhost:3000/user?ID=12345')
.then(handleResponse)
.catch(handleError)
function manageErrorConnection(err) {
if (err.response && err.response.status >= 400 && err.response.status <= 500) {
// this will trigger the `handleError` function in the promise chain
return Promise.reject(new Error('Bad status code'))
} else if (err.code === 'ECONNREFUSED') {
// this will trigger the `handlerResponse` function in the promise chain
// bacause we are not returning a rejection! Just an example
return 'nevermind'
} else {
// this will trigger the `handleError` function in the promise chain
return Promise.reject(err)
}
}
function handleResponse(response) {
console.log(`handleResponse: ${response}`);
}
function handleError(error) {
console.log(`handleError: ${error}`);
}
So in order to run an optional step, you need to:
put logic in the handler, to skip it
put the handler in the chain when it is needed (I would just avoid that since it is "spaghetti software")
put the logic in the handler example
// .... changing this line ...
return Promise.reject('nevermind')
// ....
function handleError(error) {
if (error === 'nevermind') {
return
}
console.log(`handleError: ${error}`);
}
This logic could be isolated:
axios.get('http://google.it/user?ID=12345')
.then(handleResponse)
.catch(shouldHandleError)
.catch(handleError)
function manageErrorConnection(err) { return Promise.reject('nevermind') }
function handleResponse(response) { console.log(`handleResponse: ${response}`); }
function shouldHandleError(error) {
if (error === 'nevermind') {
// this stop the chain
console.log('avoid handling');
return
}
return Promise.reject(error)
}
function handleError(error) { console.log(`handleError: ${error}`); }

Related

how to catch error of an asyn function call

I don't succeed catching the error that a async function returns. Using Vuejs 3 with a pinia store, althought I don't think that this is specific to vue or pinia.
In a pinia store I have this function:
const getAccount = async(id, month, year) => {
try {
getData.defaults.headers.common['__authorization__'] = UserStore.jwt
const response = await getData.get(`/use/b/comptes/${id}/${month}/${year}`)
if (response.status === 200) {
// update store state and:
return true
}
return false
} catch (error) {
// => this gets correctly executed when the server responds with 409
// console.error(`erreur catch : ${error.response.data.error}`)
return error.response.data
}
}
I'm calling this function from a component, like so:
watchEffect(() => {
if (route.name === 'comptes') {
getAccount( compte.value.id, route.params.month, route.params.year )
.then(result => { console.log('result', result.error) })
.catch(err => {
// this never gets executed, including when server returns a 409
console.log('err', err)
})
}
})
In other words: in the function call, only the then block gets executed, not the catch.
How do I catch the error of the first function?

How to retry fetch in a loop when it throws an error

I have the following JS function:
func() {
return fetch({
...
}).then({
...
})catch({
...
})
}
In it I return a promise returned by fetch(). In the event that it fails (ie calls catch() block) I want to repeat the whole thing. Something like having the whole thing in a while (true) loop, but I can't figure out how to do this with promises.
Any suggestions?
you should have a close look to promises and async await.
async function fetchUntilSucceeded() {
let success = false;
while(!success) {
try {
let result = await fetch(...);
success = true;
//do your stuff with your result here
} catch {
//do your catch stuff here
}
}
}
If you just need the results:
async function fetchUntilSucceeded() {
while(true) {
try {
return await fetch(...);
}
}
}
But be careful with such code as it might never resolve! also it can send a lot of requests without any waittime in between.
You can simply write a loop and count down the attempts until one succeeds or you run out. async/await makes this easy. See below for a minimal, complete example.
Note that the fetch API uses the response.ok flag to ensure that the response status falls in the 200 range. Wrapping with a try/catch is only sufficient to cover connection failures. If the response indicates a bad request, a retry is likely inappropriate. This code resolves the promise in such cases but you could consider !response.ok as an error and retry if you wish.
const fetchWithRetry = async (url, opts, tries=2) => {
const errs = [];
for (let i = 0; i < tries; i++) {
// log for illustration
console.log(`trying GET '${url}' [${i + 1} of ${tries}]`);
try {
return await fetch(url, opts);
}
catch (err) {
errs.push(err);
}
}
throw errs;
};
fetchWithRetry("https://httpstat.us/400")
.then(response => console.log("response is OK? " + response.ok))
.catch(err => console.error(err));
fetchWithRetry("foo")
.catch(err => console.error(err.map(e => e.toString())));
fetchWithRetry("https://httpstat.us/200")
.then(response => response.text())
.then(data => console.log(data))
.catch(err => console.error(err));
Pass the tries parameter as -1 if you want an infinite number of retries (but this doesn't seem like the common case to me).

next() function in beforeEach not working router.beforeEach vue js

I used the router.beforeEach but call to next() function is not working. The next variable is returning error. This is the function:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
console.log(next);
next() // make sure to always call next()!
}
})
This is the error that is shown in log as value of next variable ...
ƒ (to) {
if (to === false || isError(to)) {
// next(false) -> abort navigation, ensure current URL
this$1.ensureURL(true);
abort(to);
.....

IF in Redux Observable epic

I have an epic that catch each dispatch of getting status (just item from state, like state.process:{ status: fail, success, inWork}, not an request status like 200, 500 etc).
When status == success (by getting status from state) i need to dispatch another action like SET_STATUS_SUCCESS
const getStatus = (action, state) =>
action.pipe(
ofType(GET_STATUS),
withLatestFrom(state),
mergeMap(([action, state]) => {
const { status } = state.api.process; //here is what i need, there is no problem with status.
if (status === "success") {
return mapTo(SET_STATUS_SUCCESS) //got nothing and error.
}
})
);
Now i receive error:
Uncaught TypeError: You provided 'function (source) { return
source.lift(new MapToOperator(value)); }' where a stream was expected.
You can provide an Observable, Promise, Array, or Iterable.
at subscribeTo (subscribeTo.js:41)
What should i do? I tried just return setStatusSuccess action but it doesn't work too.
You need to return an observable from the function you pass to mergeMap. Try this:
const getStatus = (action, state) =>
action.pipe(
ofType(GET_STATUS),
withLatestFrom(state),
mergeMap(([action, state]) => {
const { status } = state.api.process;
if (status === 'success') {
return of({ type: SET_STATUS_SUCCESS });
} else {
return EMPTY;
}
}),
);
of and EMPTY are imported from rxjs.

Which RxJS operator to choose to handle HTTP errors: tap or catchError?

/* error handler that will be used below in pipe with catchError()
* when resource fetched with HttpClient get() */
private _handleError<T> (operation: string, result?:T) {
return( error: any): Observable<T> => {
console.error( operation + ' ' + error.message );
// or something else I want to do
return of(result as T); // lets me return innocuous results
}
}
getObjects() {
return this.http.get<any[]>(this.myUrl).pipe(
catchError(this._handleError('my error', [])
);
}
now using tap to handle errors
getObjects() {
return this.http.get<any[]>(this.myUrl).pipe(
tap( objects => {
// whatever action like logging a message for instance
}, err => {
console.error(err);
// whatever else I want to do
})
);
}
Why should I choose one approach instead of the other? Will handling HTTP errors with tap() keep my app' running in case they occur?
tap is to cause side effects.
catchError is to catch errors in a stream and try to handle them.
Therefore if you want to handle errors of http requests use catchError.
http.get('https://test.com/').pipe(
tap({
next: () => {
// 200, awesome!, no errors will trigger it.
},
error: () => {
// error is here, but we can only call side things.
},
}),
catchError(
(error: HttpErrorResponse): Observable<any> => {
// we expect 404, it's not a failure for us.
if (error.status === 404) {
return of(null); // or any other stream like of('') etc.
}
// other errors we don't know how to handle and throw them further.
return throwError(error);
},
),
).subscribe(
response => {
// 200 triggers it with proper response.
// 404 triggers it with null. `tap` can't make 404 valid again.
},
error => {
// any error except 404 will be here.
},
);

Resources