How to cancel token using a custom Axios instance? - javascript

I have an custom Axios instance using axios.create(). I would like to use the cancellation feature of Axios but the request fired from custom instance never gets cancelled. It does't get detected in the .isCancel() method. But it works fine when used with the global Axios object.
const axiosAuth = axios.create();
const cancelToken = axios.CancelToken.source();
//request
const getProducts = async () => {
try {
const response = await axiosAuth.get('api', {
cancelToken: cancelToken.token
});
if (response.status === 200) {
return response.data;
}
} catch (err) {
if (axios.isCancel(err)) {
console.log('Error: ', err.message);
return true;
} else {
throw new Error(err);
}
}
};
// I'm cancelling the request on button click using `cancelToken.cancel()`
I don't understand why cancellation doesn't work with a custom Axios instance.

Figured it out there was an issue in the one the Interceptors. Just make sure you check if its cancellation error there as well using Axios.isCancel() before you do anything with the error object.

Related

React Native fetch abortController - figuring out abort reason

I'm in need of implementing following features for network/http client using fetch API.
Timeout
Also abort a previous request when user make multiple requests.
I was able to implement both using the abortController. But on the case of "Timeout" (no 1), I want to catch the abort and show a proper error message with "retry" option.
But when I wrap my network request inside try catch, I can't distinguish between above 1 and 2 cases. Cause both abort are thrown with same exception name/message.
The web implementation does support passing a "reason" into the abort() call. But looks like reactNative doesn't have that implemented ( Using react-native 0.63.3 )
async function request(url, abortController) {
// Manually timing out, as I did not find any support for timeout on react-native
const timeoutRef = setTimeout(() => abortController.abort(), 90000); // CASE 1 : Timeout abort
return await fetch(url,
{
signal: controller.signal
})
}
var abortController = null;
var requestPending = false;
async function searchWebsite(searchQuery) {
// If there is already pending requesting - we cancel that previous
// pending request.
if ( abortController && !controller.signal.aborted && requestPending) {
abortController.abort(); // CASE 2 : abort previous request
}
// Create a new request
try {
abortController = new AbortController();
requestPending = true;
let apiRequest = await request("http://someurl.com", abortController);
// Do whatever with `apiRequest`
requestPending = false;
} catch(e) {
requestPending = false;
if (e.name == 'AbortError') {
// HERE I'M STRUGGLING WITH
// figure out how to distinguish between "timeout" and "previous request" abort
}
}
}
How can I distinguish between different type of abortController abort on react-native?
I was in this situation before and decided to move the cancellation logic to the async function itself instead of the fetch, and create another abort controller instance, then you can throw a different message based on which abort controller you used to abort the request, based on that message you'll know which abort controller caused the error (the cancellation)
Here is an example of what i did (in a react hook), but you should be able to apply the same logic and throw your own errors in whichever way you like
return new Promise((resolve, reject) => {
if (abortControllerCancel.current.signal.aborted) {
reject({
message: 'canceled',
reason: 'canceled',
});
}
if (abortControllerDuplicate.current.signal.aborted) {
reject({
message: 'canceled',
reason: 'duplicate',
});
}
// the rest of the async function and resolving the promise
abortControllerCancel.current.signal.addEventListener(
'abort',
() => {
reject({
message: 'canceled',
reason: 'canceled',
});
}
);
abortControllerDuplicate.current.signal.addEventListener(
'abort',
() => {
reject({
message: 'canceled',
reason: 'duplicate',
});
}
);
}

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
}

ReactJS - Javascript try/catch shows an error in the console

I'm pretty new on React and I'm learning this language nowadays.
For this purpose I'm building a test project on which I try to encounter the main classical issues and looking to solve it.
For most of React developpers, there are no difficulties in following code but I will give a few details for better comprenhension.
I have a portion of javascript code that is returning a list of articles from a Symfony Backend API only if user is authorized for getting it (Authorization via JWT will be done later). A getArticles function returns a Promise that tries to get the articles from the Symfony backend inside a try {} catch (error) {} block.
Voluntarily, Authorization token is not send to trigger an error in the query.
As the axios.get is located inside a try {} catch (error) {} block, I am surprised that an error appears in the console for the request. It doesn't impact the behavior but it is not very clean to have these errors in the console.
My question(s) :
Why an error appears in the console while the code is inside a try/catch ? To get a cleaner app behavior, is there a way to avoid having this error in the console ? I have found other React try/catch issues but I didn't deduct the similarity with my issue. Am I missing something ?
Thanks in advance ;-)
I am aware that my code could be refactored, do not hesitate to suggest any good practice
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => { return new Promise( (resolve, reject)=> {
try{
const data = axios.get('https://xxxxx/api/articles');
resolve(data);
} catch (err) {
reject(err);
}
});
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}
You can try in this way and Async functions itself returns a promise, you don't need to return a new Promise manually.
async componentDidMount() {
try {
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => req);
const getArticles = async () => {
try {
const data = axios.get('https://xxxxx/api/articles');
this.setState({ errorOnArticlesLoading: false, articles: data.data.items });
} catch (err) {
this.setState( {errorOnArticlesLoading:true} );
}
};
await getArticles()
} catch(err) {
console.log('Handled root error')
}
}
It seems that there are no solutions to avoid the 401 http error code in the console because it it printed by Chrome itself: See discussion here. So the following code cannot avoid the 401 error status to be printed in the console.
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => {
const data = await axios.get('https://xxxx/api/articles');
return data;
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}

How do I cancel a ongoing GET request with axios

I have an inputfield, onChange it sends my value of the inputfield to an API. So, the api will start fetching all the data. but when I continue typing again, I want that previous request to be canceled.
I'm using axios for making my request and tried looking at the documentation but I can't seem to figure out how it really works, can someone explain how to do this?
Here is my function that gets called by every new input:
const onChange = (value) => {
setTimeout(async() => {
let result = []
if (y === "keyword") result = await AutoSuggestionKeyword({
value: value
});
else {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
await axios.get(`https://${api_uri}/${value.toLowerCase()}`)
.catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
}).then(resp => {
console.log(resp)
});
source.cancel();
}
}, 500)
}
You need to provide a cancelToken in your request,
axios.get(`https://${api_uri}/${value.toLowerCase()}`, {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
I don't think you can cancel an HTTP request, but what you can do is wrap it in debounce function, debounce function wait for a certain time before calling the callback or function you wrap or pass on it.
you can simply google debounce and it will give you articles or npm packages that you can use.
I think this article also has the same issue you are trying to resolve
Happy coding
Edit 1: yeah so you can cancel the http request see comment below

Handling network errors with axios and Twilio

I have an application that uses axios for it's ajax requests. When a user experiences a network issue (for example, their wifi goes out and they no longer have an internet connection while on my application), I want to make sure that only the first axios request is made, and if I detect there is a network issue, to not attempt any more requests, but instead to retry the same request until successful.
My application performs many requests, including a request every 2.5 seconds (in this example, getData). It also establishes a Twilio connection when the application initializes (it executes twilio() on initialization).
When a connection is lost, the following happens:
getData fails, resulting in a console message of this is a network error.
TwilioDevice.offline is executed. This results in two error messages: first a this is a network error. message (error message #2) when TwilioDevice.offline tries fetchToken(), and then a received an error. message (error message #3) after the fetchToken() fails.
Given #'s 1 and 2, how can I make sure that:
If I experience a network error, I only receive one error message instead of 3 saying that "there was a network error"
My app detects that there is a network error, then tries to re-establish a connection, then, if successful, resumes fetching data, Twilio tokens, etc.
Thanks! Code is below.
example code:
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
handleError(error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
return handleError(error);
}
};
const handleError = error => {
if (!error.response) {
console.log("this is a network error.");
} else {
console.log("received an error.");
}
};
twilio.js:
import { Device as TwilioDevice } from 'twilio-client';
const registerEvents = () => {
TwilioDevice.ready(() => {
console.log('Twilio.Device is now ready for connections');
});
TwilioDevice.connect((conn) => {
console.log(`Connecting call with id ${conn.parameters.CallSid}`);
// code to start call
conn.disconnect((connection) => {
console.log(`Disconnecting call with id ${connection.parameters.CallSid}`);
// code to end call
});
});
TwilioDevice.error((error) => {
console.log("Twilio Error:");
console.log(error);
});
TwilioDevice.offline(async () => {
try {
const newTwilioToken = await fetchToken(); // error message #2
return TwilioDevice.setup(newTwilioToken);
} catch (error) {
return handleError(error); // error message #3
}
});
};
export const twilio = async () => {
try {
registerEvents();
const twilioToken = await fetchToken();
TwilioDevice.setup(twilioToken);
} catch (error) {
return handleError(error);
}
};
I would recommend making your fetchToken and getData methods to throw errors rather than handling it themselves so that they can be handled by their outer functions.
Something like,
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
throw (error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
throw (error);
}
};
So that when you call twilio() that function can handle the error like retrying etc.

Categories

Resources