Axios cancel Request shows browser alert which stops the execution until you click ok.
I want to cancel my requests, all my Api calls are in separate file named apiCalls.js.
Component with cancelToken.
componentDidMount() {
const CancelToken = axios.CancelToken;
// create the source
this.source = CancelToken;
}
persistCancel = (cancel) => {
this.setState({cancels: [...this.state.cancels, cancel]})
}
componentWillUnmount(){
this.state.cancels.forEach((c) => c());
}
this is my Api call from component.
getScoreCardCall({profileId, campaignIds, startDate, endDate}, (scoreCards) => {
//success
this.setState({
scoreCards,
showComcardsLoader: false
})
},this.source,this.persistCancel);
and in the apiCalls.js
export function getScoreCardCall(params,callback, source,onRequest){
axios.get(url,
{
cancelToken: new source(function executor(c) {
onRequest(c);
}),
params:{
profileId: params.profileId,
campaignId: params.campaignIds.toString(),
startDate: params.startDate,
endDate: params.endDate,
}
})
.then(res => {
if(callback != null){
if(res.data.length!=0){
callback(res.data);
}
}
})
.catch(err => {
if (axios.isCancel(err)) {
console.log(err.message);
}
})
}
Can someone please tell me why is alert showing with every request cancellation?? or what i am doing wrong?
axios#cancellationdescribes two ways to use the cancelToken. You used the first way, with source.token/source.cancel. I started out that way, and had a similar problem as yours: Once a request to a particular URL was canceled, I could never get a successful response from that URL again. I switched to the second method using an executor function and the problem went away. I guess was sharing the same cancelToken for multiple requests, which is what they say you can do with the executor function method. Anyway, maybe that would work for you too.
Related
I have created a redux that is going to request an API and if the result is 200, I want to redirect the user to another page using history.
The problem is: I don't know how to trigger this change if the action is a success.
I could redirect the user in my useCase function but I can't use history.push pathName/state argument because it only works in a React component.
So this is what I have done in my React component:
const acceptProposalHandler = () => {
store.dispatch(acceptProposal(id)).then(() => {
setTimeout(() => {
if (isAccepted) { //isAccepted is false by default but is changed to true if the
//request is 200
history.push({
pathname: urls.proposal,
state: {
starterTab: formatMessage({id: 'proposalList.tabs.negotiation'}),
},
});
}
}, 3000);
});
};
Sometimes it works but other times it wont. For some reason, .then is called even if the request fails.
I'm using setTimeOut because if I don't, it will just skip the if statement because the redux hasn't updated the state with isAccepted yet.
This is my useCase function from redux:
export const acceptProposal = (id: string) => async (
dispatch: Dispatch<any>,
getState: () => RootState,
) => {
const {auth} = getState();
const data = {
proposalId: id,
};
dispatch(actions.acceptProposal());
try {
await API.put(`/propostas/change-proposal-status/`, data, {
headers: {
version: 'v1',
'Content-Type': 'application/json',
},
});
dispatch(actions.acceptProposalSuccess());
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
}
};
What I'm doing wrong? I'm using Redux with thunk but I'm not familiar with it.
".then is called even if the request fails." <- this is because acceptProposal is catching the API error and not re-throwing it. If an async function does not throw an error, it will resolve (i.e. call the .then). It can re-throw the error so callers will see an error:
export const acceptProposal = (id: string) => async (
// ... other code hidden
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
// ADD: re-throw the error so the caller can use `.catch` or `try/catch`
throw error;
}
};
JavaScript----.I have an api response which should come before anything else loads up or renders in the browser but it gives response which is slower than the loading of first component which rendered on the page.
This the code for the api call which i make to get the data is given below.Is there any way i can make this given below to be executed synchronously.
const GetLanguageData = (pageName) => {
const traslationApiUrlObj = {
languageCode: $('#hdnLanguageCode').val(),
baseUrl: `${apiUrl}`,
page: pageName
}
const { baseUrl, languageCode, page } = traslationApiUrlObj;
const traslationApiUrl = `${baseUrl}${languageCode}/${page}`;
ResourceLanguageText = null;
fetch(traslationApiUrl)
.then(res => res.json())
.then((result) => {
ResourceLanguageText = result;
console.log('Result');
},
(error) => {
console.log(error);
});
}
it can be achieved using combination of state and componentdidmount() lifecycle method.
keep a boolean flag in state "loaded" initialized to true.
call api in componentdidmount(), after you receive response set "loaded" to true in state.
in your render() method, do a conditional render based on your loaded is true
I am trying to implement the following logic: call login then if response is ok, call method for retrieving user data.
Login action
loginUser({commit,dispatch}, credentials) {
const form = new URLSearchParams();
form.append("login", credentials.login);
form.append("password", credentials.password);
const formConfig = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
return Axios.post(loginUrl, form, formConfig).then(
(response) => {
commit('setErrorMessage', '', {root: true});
commit('setAuthenticated', response.headers[authorization]);
dispatch('getUserByLoginAuth',credentials.login);
},
(error) => {
if (error.response.status===500){
commit('setErrorMessage', error.response.data.message, {root: true});
} else {
commit('setErrorMessage', error.response.data, {root: true});
}
});
},
The second action dispatched from the one above:
getUserByLoginAuth({commit, getters}, login) {
return getters.authenticatedAxios.get(userUrl + '/find', {
params: {
login: login
}
}).then(
(response) => {
commit('setErrorMessage', '', {root: true});
commit('setUser', response.data);
},
(error) => {
commit('setErrorMessage', error.response.data, {root: true});
});
},
This action is called from the second time only(as I understand it is related to promise).
Here is a code from component which dispatches login action
this.$store.dispatch('loginUser', this.credentials).then(() => {
this.errorMessage = this.getError;
if (this.errorMessage.length) {
this.errorOccurred = true;
}
this.$router.push({path: '/user/' + this.getId});
});
this.errorOccurred = false;
},
Here also I am not sure if I am doing routing in correct place. As I understand then will work with promise from getUser so errorMessage from login might be lost. I would like to prevent it and make dispatch of getUser correctly from the first time
I don't entirely follow what you're asking but this seems likely to be a problem:
dispatch('getUserByLoginAuth',credentials.login);
The problem isn't the call itself. The problem is that it's kicking off a new asynchronous action without chaining it onto the existing promises. From the perspective of loginUser everything is done, it won't wait for getUserByLoginAuth.
The result will be that the then in your component will be called before getUserByLoginAuth is done. I would imagine this is why it seems to work the second time, because it's picking up the relevant data from the previous call.
The solution would be simply to change it to:
return dispatch('getUserByLoginAuth',credentials.login);
By putting in a return it adds it to the promise chain, so loginUser won't be treated as complete until getUserByLoginAuth is done.
So I am implementing axios call cancelation in the project. Right now looking at axios documentation it seems pretty straight forward https://github.com/axios/axios#cancellation
So I did define variables on the top of my Vue component like
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
obviously on top of that is import axios from 'axios';
Then I have a method of fetching the API
On the top of the method I want to cancel out the request in case it is running so the last one cancels out if the user spams the filtering.
async fetchPartners(inputToClear) {
source.cancel();
...
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, { type: 'error' });
return [];
} finally {
...
}
},
So it is pretty straight forward, just took the code from axios documentation I gave you above, it should be working by logic. But what is actually happening, it doesn't even allow me to fetch the call, it is already cancelled out before I can call it. On console it shows me
Request canceled undefined
It just catches the error as if I am cancelling the call, but how can it be, because I am source.cancel() before the call.
Anyone has any idea?
I hope you should throttle your requests instead of canceling the request.
Could you please try the following if throttle does not suit your requirement?
const CancelToken = axios.CancelToken;
let source;
async fetchPartners(inputToClear) {
if(source){
source.cancel();
}
...
source = CancelToken.source();
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, {
type: 'error'
});
return [];
} finally {
...
}
}
I'm building a PWA using the Vue CLI 3 PWA plugin, and I'm not sure how to handle the response from an AJAX call made while offline.
All I have is basically sample code from the Workbox documentation...
// service-worker.js
const queue = new workbox.backgroundSync.Queue('CR_OfflineQueue')
self.addEventListener('fetch', (event) => {
const promiseChain = fetch(event.request.clone())
.catch((err) => {
console.log('Queued Event:', event.request)
return queue.addRequest(event.request)
})
event.waitUntil(promiseChain)
})
The AJAX is just a basic Axios get() that's called when the store is loaded and in a test mutation
// store.js
getUsername () {
return axios.get(
'http://localhost:8005/username/',
{
responseType: 'json',
withCredentials: true
})
}
const store = new Vuex.Store({
state: {
username: null
},
mutations: {
test_api (state) {
getUsername()
.then((res) => {
state.username = res.username
})
.catch((err) => {
console.error('getUsername:', err)
})
}
}
})
The request is being successfully replayed, but I have no idea what to do with it, and I'm unable to find an example using workbox.backgroundSync.Queue.
As far as getUsername() is concerned, the request failed, but how do I have the calling function essentially pick up where it left off?
I don't think that need something to check the queue for the request at a regular interval to "manually" re-trigger (not via the auto replay). How would I use the queue class to take the result from the replayed request and set the username property?