How do I cancel a ongoing GET request with axios - javascript

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

Related

jquery ajaxComplete equivalent in axios

Started learning axios and I'm loving it!
Quick question, could not find an answer for it, maybe there is non.
In jQuery ajax there is a method called ajaxComplete, I was wandering if there is an equivalent in axios?
axios uses promises. You can use
axios.get(url,[options]).then(res=>{
/* hande it here */
})
Check out how js promises work if you do not have a basic knowledge https://web.dev/promises/
For global handling axios event this may help https://auralinna.blog/post/2019/global-http-request-and-response-handling-with-the-axios-interceptor/
No there is not. jQuery's $.ajax has a function build in where it fires an event when a request has been finished. This is the ajaxComplete event.
Axios does not have such behavior, but you could build your own with the CustomEvent interface. Or / and assume that there is a method axiosSuccess on the document and call that.
const axiosGet = async url => {
try {
const response = await axios.get(url)
const axiosSuccessEvent = new CustomEvent('axiossuccess', {
detail: { url, response }
})
document.dispatchEvent(axiosSuccessEvent)
if (typeof document.axiosSuccess === 'function') {
document.axiosSuccess({ url, response })
}
return response
} catch (error) {
console.log(error)
}
}
And then listen for your own event on the document.
document.addEventListener('axiossuccess', event => {
const { detail } = event
const { url, response } = detail
console.log(url, response)
})
document.axiosSuccess = ({ url, response }) => {
console.log(url, response)
}
axiosGet('https://stackoverflow.com/questions/63998921/')

How to cancel token using a custom Axios instance?

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.

How to cancel http request properly in Node.js?

I need to implement a cancel-able client-side HTTP request in Node.js, without using external libraries. I'm giving a Promise object - cancellationPromise - which gets rejected when the cancellation is externally requested. This is how I know I may need to call request.abort().
The question is, should I be calling request.abort() only if https.request is still pending and response object is not yet available?
Or, should I be calling it even if I already got the response object and am processing the response data, like in the code below? In which case, will that stop any more response.on('data') events from coming?
async simpleHttpRequest(url, oauthToken, cancellationPromise) {
let cancelled = null;
let oncancel = null;
cancellationPromise.catch(error => {
cancelled = error; oncancel && oncancel(error) });
try {
const response = await new Promise((resolve, reject) => {
const request = https.request(
url.toString(),
{
method: 'GET',
headers: { 'Authorization': `Bearer ${oauthToken}` }
},
resolve);
oncancel = error => request.abort();
request.on('error', reject);
request.end();
});
if (cancelled) throw cancelled;
// do I need "oncancel = null" here?
// or should I still allow to call request.abort() while fetching the response's data?
// oncancel = null;
try {
// read the response
const chunks = await new Promise((resolve, reject) => {
response.on('error', reject);
const chunks = [];
response.on('data', data => chunks.push(data));
response.on('end', () => resolve(chunks));
});
if (cancelled) throw cancelled;
const data = JSON.parse(chunks.join(''));
return data;
}
finally {
response.resume();
}
}
finally {
oncancel = null;
}
}
It depends what you want to achieve by aborting a request.
Just a bit of background. HTTP 1 is not able to "cancel" a request it sends it and then waits for the response. You cannot "roll back" the request you did. You need a distributed transaction to do so. (Further reading.) As the MDN developer document states:
The XMLHttpRequest.abort() method aborts the request if it has already been sent. When a request is aborted, its readyState is changed to XMLHttpRequest.UNSENT (0) and the request's status code is set to 0.
Basically you stop the response from being processed by your application. The other application will probably (if you called abort() after it was sent to it) finish its processing anyways.
From the perspective of the question:
The question is, should I be calling request.abort() only if https.request is still pending and response object is not yet available?
TL.DR.: It only matters from the point of view of your application. As I glance at your code, I think it will work fine.

Returning data from Axios API [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 months ago.
I am trying to use a Node.JS application to make and receive API requests. It does a get request to another server using Axios with data it receives from an API call it receives. The second snippet is when the script returns the data from the call in. It will actually take it and write to the console, but it won't send it back in the second API.
function axiosTest() {
axios.get(url)
.then(function (response) {
console.log(response.data);
// I need this data here ^^
return response.data;
})
.catch(function (error) {
console.log(error);
});
}
...
axiosTestResult = axiosTest();
response.json({message: "Request received!", data: axiosTestResult});
I'm aware this is wrong, I'm just trying to find a way to make it work. The only way I can seem to get data out of it is through console.log, which isn't helpful in my situation.
The issue is that the original axiosTest() function isn't returning the promise. Here's an extended explanation for clarity:
function axiosTest() {
// create a promise for the axios request
const promise = axios.get(url)
// using .then, create a new promise which extracts the data
const dataPromise = promise.then((response) => response.data)
// return it
return dataPromise
}
// now we can use that data from the outside!
axiosTest()
.then(data => {
response.json({ message: 'Request received!', data })
})
.catch(err => console.log(err))
The function can be written more succinctly:
function axiosTest() {
return axios.get(url).then(response => response.data)
}
Or with async/await:
async function axiosTest() {
const response = await axios.get(url)
return response.data
}
Guide on using promises
Info on async functions
I know this post is old. But i have seen several attempts of guys trying to answer using async and await but getting it wrong. This should clear it up for any new references
UPDATE: May 2022
This answer is still having lots of interest and have updated it to use arrow functions
const axiosTest = async () {
try {
const {data:response} = await axios.get(url) //use data destructuring to get data from the promise object
return response
}
catch (error) {
console.log(error);
}
}
you can populate the data you want with a simple callback function,
let's say we have a list named lst that we want to populate,
we have a function that pupulates pupulates list,
const lst = [];
const populateData = (data) => {lst.push(data)}
now we can pass the callback function to the function which is making the axios call and we can pupulate the list when we get data from response.
now we make our function that makes the request and pass populateData as a callback function.
function axiosTest (populateData) {
axios.get(url)
.then(function(response){
populateData(response.data);
})
.catch(function(error){
console.log(error);
});
}
The axios library creates a Promise() object. Promise is a built-in object in JavaScript ES6. When this object is instantiated using the new keyword, it takes a function as an argument. This single function in turn takes two arguments, each of which are also functions — resolve and reject.
Promises execute the client side code and, due to cool Javascript asynchronous flow, could eventually resolve one or two things, that resolution (generally considered to be a semantically equivalent to a Promise's success), or that rejection (widely considered to be an erroneous resolution). For instance, we can hold a reference to some Promise object which comprises a function that will eventually return a response object (that would be contained in the Promise object). So one way we could use such a promise is wait for the promise to resolve to some kind of response.
You might raise we don't want to be waiting seconds or so for our API to return a call! We want our UI to be able to do things while waiting for the API response. Failing that we would have a very slow user interface. So how do we handle this problem?
Well a Promise is asynchronous. In a standard implementation of engines responsible for executing Javascript code (such as Node, or the common browser) it will resolve in another process while we don't know in advance what the result of the promise will be. A usual strategy is to then send our functions (i.e. a React setState function for a class) to the promise, resolved depending on some kind of condition (dependent on our choice of library). This will result in our local Javascript objects being updated based on promise resolution. So instead of getters and setters (in traditional OOP) you can think of functions that you might send to your asynchronous methods.
I'll use Fetch in this example so you can try to understand what's going on in the promise and see if you can replicate my ideas within your axios code. Fetch is basically similar to axios without the innate JSON conversion, and has a different flow for resolving promises (which you should refer to the axios documentation to learn).
GetCache.js
const base_endpoint = BaseEndpoint + "cache/";
// Default function is going to take a selection, date, and a callback to execute.
// We're going to call the base endpoint and selection string passed to the original function.
// This will make our endpoint.
export default (selection, date, callback) => {
fetch(base_endpoint + selection + "/" + date)
// If the response is not within a 500 (according to Fetch docs) our promise object
// will _eventually_ resolve to a response.
.then(res => {
// Lets check the status of the response to make sure it's good.
if (res.status >= 400 && res.status < 600) {
throw new Error("Bad response");
}
// Let's also check the headers to make sure that the server "reckons" its serving
//up json
if (!res.headers.get("content-type").includes("application/json")) {
throw new TypeError("Response not JSON");
}
return res.json();
})
// Fulfilling these conditions lets return the data. But how do we get it out of the promise?
.then(data => {
// Using the function we passed to our original function silly! Since we've error
// handled above, we're ready to pass the response data as a callback.
callback(data);
})
// Fetch's promise will throw an error by default if the webserver returns a 500
// response (as notified by the response code in the HTTP header).
.catch(err => console.error(err));
};
Now we've written our GetCache method, lets see what it looks like to update a React component's state as an example...
Some React Component.jsx
// Make sure you import GetCache from GetCache.js!
resolveData() {
const { mySelection, date } = this.state; // We could also use props or pass to the function to acquire our selection and date.
const setData = data => {
this.setState({
data: data,
loading: false
// We could set loading to true and display a wee spinner
// while waiting for our response data,
// or rely on the local state of data being null.
});
};
GetCache("mySelelection", date, setData);
}
Ultimately, you don't "return" data as such, I mean you can but it's more idiomatic to change your way of thinking... Now we are sending data to asynchronous methods.
Happy Coding!
axiosTest() needs to return axios.get, which in turn returns a Promise.
From there, then can be used to execute a function when said Promise resolves.
See Promise for more info.
Alternatively, await can be used from within the scope of some async function.
// Dummy Url.
const url = 'https://jsonplaceholder.typicode.com/posts/1'
// Axios Test.
const axiosTest = axios.get
// Axios Test Data.
axiosTest(url).then(function(axiosTestResult) {
console.log('response.JSON:', {
message: 'Request received',
data: axiosTestResult.data
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
IMO extremely important rule of thumb for your client side js code is to keep separated the data handling and ui building logic into different funcs, which is also valid for axios data fetching ... in this way your control flow and error handlings will be much more simple and easier to manage, as it could be seen from this
ok fetch
and this
NOK fetch
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function getUrlParams (){
var url_params = new URLSearchParams();
if( window.location.toString().indexOf("?") != -1) {
var href_part = window.location.search.split('?')[1]
href_part.replace(/([^=&]+)=([^&]*)/g,
function(m, key, value) {
var attr = decodeURIComponent(key)
var val = decodeURIComponent(value)
url_params.append(attr,val);
});
}
// for(var pair of url_params.entries()) { consolas.log(pair[0]+ '->'+ pair[1]); }
return url_params ;
}
function getServerData (url, urlParams ){
if ( typeof url_params == "undefined" ) { urlParams = getUrlParams() }
return axios.get(url , { params: urlParams } )
.then(response => {
return response ;
})
.catch(function(error) {
console.error ( error )
return error.response;
})
}
// Action !!!
getServerData(url , url_params)
.then( response => {
if ( response.status === 204 ) {
var warningMsg = response.statusText
console.warn ( warningMsg )
return
} else if ( response.status === 404 || response.status === 400) {
var errorMsg = response.statusText // + ": " + response.data.msg // this is my api
console.error( errorMsg )
return ;
} else {
var data = response.data
var dataType = (typeof data)
if ( dataType === 'undefined' ) {
var msg = 'unexpected error occurred while fetching data !!!'
// pass here to the ui change method the msg aka
// showMyMsg ( msg , "error")
} else {
var items = data.dat // obs this is my api aka "dat" attribute - that is whatever happens to be your json key to get the data from
// call here the ui building method
// BuildList ( items )
}
return
}
})
</script>
After 6 hours of fluttering, I realized it was a one-line problem. If you are interfering with the axios life-cycle, you may have forgotten this line:
componentDidMount() {
this.requestInterceptor = axios.interceptors.request.use((request) => {
this.updateApiCallFor(request.url, true);
return request;
});
this.responseInterceptor = axios.interceptors.response.use((response) => {
this.updateApiCallFor(response.config.url, false);
return response; // THIS LINE IS IMPORTANT !
}, (error) => {
this.updateApiCallFor(error.config.url, false);
throw error;
});
async makes a function return a Promise
await makes a function wait for a Promise
code async/await
// https://www.npmjs.com/package/axios
const axios = require('axios')
/* --- */
async function axiosTest() {
let promiseAxios = axios.get( 'https://example.com' )
/* --- */
console.log( await promiseAxios )
}
/* --- */
axiosTest()
replit.com Stackoverflow - Returning data from Axios API
replit.com Stackoverflow - How to return values from async
code async/await with return
// https://www.npmjs.com/package/axios
const axios = require('axios')
/* --- */
async function axiosTest() {
console.log( await promiseAxios() )
}
/* --- */
axiosTest()
/* --- */
// create function for promise axios and return it
function promiseAxios() {
return axios.get( 'https://example.com' )
}
replit.com Stackoverflow - Returning data from Axios API - return
replit.com Stackoverflow - How to return values from async - return
Try this,
function axiosTest() {
axios.get(url)
.then(response => response.data)
.catch(error => error);
}
async function getResponse () {
const response = await axiosTest();
console.log(response);
}
getResponse()
It works, but each function where you want to get the response needs to be an async function or use an additional .then() callback.
function axiosTest() {
axios.get(url)
.then(response => response.data)
.catch(error => error);
}
async function getResponse () {
axiosTest().then(response => {
console.log(response)
});
}
getResponse()
If anyone knows a way to avoid this please do tell.
Also checkout Katsiaryna (Kate) Lupachova's article on Dev.to. I think it will help.
async handleResponse(){
const result = await this.axiosTest();
}
async axiosTest () {
return await axios.get(url)
.then(function (response) {
console.log(response.data);
return response.data;})
.catch(function (error) {
console.log(error);
});
}
You can find check https://flaviocopes.com/axios/#post-requests url and find some relevant information in the GET section of this post.
You can use Async - Await:
async function axiosTest() {
const response = await axios.get(url);
const data = await response.json();
}

React/Redux , how to "chain" actions for http call when http call is in middleware

I am setting up a basic app to call the server for some stuff. the one catch, is I have made my server calls as a piece of middleware so other devs I work with can use it. I can make the call - however I would like to add "loading", "success", and "error" actions to it. Before - I could easily do this by placing the calls in the action creator itself, something like this for example :
//in the action creator
export function fetchData() {
return dispatch => {
request
.get('/myApi')
.end((err, res) => {
if (err) {
dispatch({
type: LOADING_ERROR,
error: err
});
} else {
let myData = JSON.parse(res.text);
dispatch({
type: LIST_DATA,
myData
});
}
});
dispatch({
type: LOADING_DATA
});
};
this worked great for me for having the loading/success/error accessible on the ui (so i can do things like show/hide a loader and such).
In this new project, I have my calls in a piece of middleware like so :
//the middleware
export default (actionObject) => (store) => (next) => (action) => {
switch(action.type) {
case actionObject.LIST:
let constructedURL = actionObject.URL + "?query=&context=" + actionObject.context;
request
.get(constructedURL)
.end((err, res) => {
if (err) {
return next(action);
}
action[options.resultAction] = res.body;
return next(action);
});
break;
default:
return next(action);
}
}
So the actionObjectis just an object you pass to let you set up this middleware so it will work with your own actions/reducers, but it is just matching your defined action to make the call. Now the issue is I don't know how I can get back into the loading/success/error actions from here. I have them set up in my actions and reducers but I am unsure how to execute it in this manner.
I was first looking at something like this - https://gist.github.com/iNikNik/3c1b870f63dc0de67c38#file-helpers-js-L66-L80 , however I am not sure if this is exactly what I am trying to do. I am not sure where I can tell it to fire the action DATA_LOADING, and then in success of the call fire either LIST_DATA or LOADING_ERROR. Could use any advice, and thank you for reading!

Categories

Resources