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?
Related
I create a site in nuxt and got data from worpdress api.
I have a few store: home.js, solutions.js, tipo.js, portfolio.js and options.js.
In fetch i check, if the store array is empty, than call dispatch and fill arrays.
export default {
async fetch({ store }) {
try {
if (store.getters['home/home'].length === 0) {
await store.dispatch('home/fetchHome');
}
if (store.getters["solutions/getSolutions"].length === 0) {
await store.dispatch('solutions/fetchSolutions');
}
if (store.getters["tipo/getTipo"].length === 0) {
await store.dispatch('tipo/fetchTipo');
}
if (store.getters["portfolio/getPortfolio"].length === 0) {
await store.dispatch('portfolio/fetchPortfolio');
}
if(store.getters["options/getOptions"].length === 0){
await store.dispatch('options/fetchOptions');
}
} catch (e) {
console.log(e, 'e no data')
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
But the problem is, that the page is loading to long time. Because i call dispatches throw await, and i think, this is the problem.
How can i call all dispatches in fethc, without async, but parallel?
I see the advantage of working with fetch over asyncData in that only the first time when I load the page, I need to wait a little, the arrays will fill up and when I get to the current page from another page, there will be no requests through the api, and the data will be output from the store.
It's just that there is very little information on nuxt in terms of ideology, how to work and what is better to use and when. In next, this is better.
This method doesn't work.
fetch({ store }) {
const promise1 = new Promise((resolve, reject) => {
if (store.getters['home/home'].length === 0) {
resolve(store.dispatch('home/fetchHome'));
}
});
const promise2 = new Promise((resolve, reject) => {
if (store.getters["solutions/getSolutions"].length === 0) {
resolve(store.dispatch('solutions/fetchSolutions'));
}
});
const promise3 = new Promise((resolve, reject) => {
if (store.getters["tipo/getTipo"].length === 0) {
resolve(store.dispatch('tipo/fetchTipo'));
}
});
const promise4 = new Promise((resolve, reject) => {
if (store.getters["portfolio/getPortfolio"].length === 0) {
resolve(store.dispatch('portfolio/fetchPortfolio'));
}
});
const promise5 = new Promise((resolve, reject) => {
if (store.getters["options/getOptions"].length === 0) {
resolve(store.dispatch('options/fetchOptions'));
}
});
Promise.all([promise1, promise2, promise3, promise4, promise5])
.then((data) => console.log(data))
.catch((error) => console.log(error));
Assuming that:
store.dispatch() returns Promise,
the first attempt in the question is generally correct,
the objective is to perform relevant dispatches in parallel,
then:
elimitate await from the store.dispatch() sequence,
accumulate the promises returned by store.dispatch() in an array,
don't use a new Promise() wrapper,
await the Promise returned by Promise.all(promises).
export default {
async fetch({ store }) {
try {
let promises = [];
if (store.getters['home/home'].length === 0) {
promises.push(store.dispatch('home/fetchHome'));
}
if (store.getters['solutions/getSolutions'].length === 0) {
promises.push(store.dispatch('solutions/fetchSolutions'));
}
if (store.getters['tipo/getTipo'].length === 0) {
promises.push(store.dispatch('tipo/fetchTipo'));
}
if (store.getters['portfolio/getPortfolio'].length === 0) {
promises.push(store.dispatch('portfolio/fetchPortfolio'));
}
if(store.getters['options/getOptions'].length === 0) {
promises.push(store.dispatch('options/fetchOptions'));
}
let data = await Promise.all(promises);
console.log(data);
} catch (e) {
console.log(e);
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
For convenience, this can be proceduralised as follows:
export default {
async fetch({ store }) {
try {
let paths = [
{ get: 'home/home', fetch: 'home/fetchHome' },
{ get: 'solutions/getSolutions', fetch: 'solutions/fetchSolutions' },
{ get: 'tipo/getTipo', fetch: 'tipo/fetchTipo' },
{ get: 'portfolio/getPortfolio', fetch: 'portfolio/fetchPortfolio' },
{ get: 'options/getOptions', fetch: 'options/fetchOptions' }
];
let promises = paths.filter(p => store.getters[p.get].length === 0).map(p => store.dispatch(p.fetch));
let data = await Promise.all(promises);
console.log(data);
} catch (e) {
console.log(e);
}
},
components: { HomeContacts, PortofolioSlider, Clients, ChiSiamo, Solutions, HomeIntro }
}
It may make more sense to define the paths array elsewhere in the code and pass it to a simplified fetch(), giving it the profile :
fetch({ store, paths })
If it still doesn't work, then there's something your're not telling us.
Promise.all can be useful here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Or even Promise.allSettled(), depending on what you're trying to do: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
Then, this is a matter of displaying something while your page does the fetching. You could use a v-if="$fetchState.pending" at the top level of your page to display a loader while the whole thing is being fetched.
There is nothing related to ideology here, there are 2 hooks that do data fetching by either blocking the render of the page (asyncData()) or allowing you to render it while the data is fetched (fetch()).
Nothing related to the framework by itself, you're free to do as you'd like.
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}`); }
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.
/* 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.
},
);
Component InfiniteLoader from react-virtualised requires function passed as property loadMoreRows to have signature like { startIndex: number, stopIndex: number }): Promise.
I'm using redux in my project, so loadMoreRows is a redux action creator like this:
const fetchEntities(start, stop) {
return fetch(`${myUrl}&start=${start}?stop=${stop}`)
}
const loadMoreRows = ({ startIndex, stopIndex }) => {
return (dispatch, getState) => {
return function(dispatch) {
return fetchEntities(startIndex, stopIndex).then(
items => dispatch(simpleAction(items)),
error => console.log(error)
)
}
}
}
after that, this action is connected to react component containing InfiniteLoader using connect function from react-redux.
So I'm not sure, how can I satisfy signature requirement, as redux action creators don't return any value/
eyeinthebrick is correct. A Promise is not a required return value.
When you "connect" a Redux action-creator, invoking it (dispatching it) actually returns a Promise. So for example I think you could do something more like this...
function fetchEntities (start, stop) {
return fetch(`${myUrl}&start=${start}?stop=${stop}`)
}
const loadMoreRows = ({ startIndex, stopIndex }) => {
return async (dispatch, getState) => {
try {
const items = await fetchEntities(startIndex, stopIndex)
await dispatch(simpleAction(items))
} catch (error) {
console.log(error)
}
}
}
At which point InfiniteLoader can just await the returned Redux promise.