Reputation: 232
Bit of background, I have this API call that can be quite lengthy in its response (talking about over a minute in some instances, but 10-15 seconds for the most part). What I'd like to do is set a timeout on the client side while the backend continues to process the call. I'm using axios to handle http request and I know there is a timeout
key that is default is 0
meaning that theres no timeout so the call will continue until either succeeds or fails. I tried to set it to 1
to see how this would handle a one millisecond timeout and the call is cancelled...which makes sense. My question now is, how can I implement a timeout on the client side without cancelling the HTTP request?
Some code to get your head around what I've tried.
import React from "react";
import axios from "axios"
function App() {
const fetchLongRequest = async () => {
try{
// All peachy over here if no timeout is implemented...
const myRequest = await axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json"
},
})
console.log("SUCCESS!", JSON.stringify(myRequest.data, null, 2))
}catch(error){
console.log("FAIL!", error.message)
}
}
return (
<button onClick={() => fetchLongRequest()}>Fetch</button>
);
}
export default App;
now this is my introduction of the timeout
import React from "react";
import axios from "axios";
function App() {
const fetchLongRequest = async () => {
// timeout works as expected but I'd like to let the call go to the backend and do its thing.
try {
const myRequest = await axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json",
},
timeout: 1,
});
console.log("SUCCESS!", JSON.stringify(myRequest.data, null, 2));
} catch (error) {
console.log("FAIL!", error.message);
}
};
return <button onClick={() => fetchLongRequest()}>Fetch</button>;
}
export default App;
I know the request is a bit odd as it opens many questions such as error handling, how to know when this call is done, etc. I'd like to get some feedback in how I can achieve this task...please :)
Upvotes: 4
Views: 18829
Reputation: 1
All you need is a timeout set BEFORE the request
import React from "react";
import axios from "axios";
function App() {
const fetchLongRequest = async () => {
const waitTime = 5000;
setTimeout(() => console.log("Request taking a long time"), waitTime);
try {
const result = await axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json",
}
});
console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
} catch(error) {
console.log("FAIL!", error.message);
}
};
return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;
I think this will do what you want, uses Promise.race
note: this is still not quite right as far as error handling goes
the handleError
function is purely so if a the request fails before the timeout the failure isn't output twice
import React from "react";
import axios from "axios";
function App() {
const fetchLongRequest = async () => {
const waitTime = 5000;
const handleError = error => {
// this makes sure that the FAIL output isn't repeated in the case when there's a failure before the timeout
if (!error.handled) {
if (error.timedout) {
console.log("TIMEDOUT", error.timedout);
} else {
console.log("FAIL!", error.message);
error.handled = true;
throw error;
}
}
};
const makeRequest = async () => {
try {
const result = await axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json",
}
});
console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
} catch(error) {
return handleError(error);
}
};
const timer = new Promise((_, reject) => setTimeout(reject, waitTime, {timedout: "request taking a long time"}));
try {
await Promise.race([makeRequest(), timer]);
} catch(error) {
handleError(error);
}
};
return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;
As a side note, this code is far cleaner without async
/await
- though, to be fair, I'm not as fluent using async
/await
as I am with Promises alone - I've used Promises since before there was a .catch
:p
non
async
/await
implementation
import React from "react";
import axios from "axios";
function App() {
const fetchLongRequest = () => {
const waitTime = 5000;
const handleError = error => {
// this makes sure that the FAIL output isn't repeated in the case when there's a failure before the timeout
if (!error.handled) {
if (error.timedout) {
console.log("TIMEDOUT", error.timedout);
} else {
console.log("FAIL!", error.message);
error.handled = true;
throw error;
}
}
};
const myRequest = axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json",
}
}).then(result => {
console.log("SUCCESS!", JSON.stringify(result.data, null, 2));
}).catch(handleError);
const timer = new Promise((_, reject) => setTimeout(reject, waitTime, {timedout: "request taking a long time"}));
return Promise.race([myRequest, timer]).catch(handleError);
};
return <button onClick = {() => fetchLongRequest()}>Fetch </button> ;
}
export default App;
Of course "cleaner" is just my opinion
Upvotes: 11
Reputation: 667
Axios API call is promise-based
so you could simply use then
and catch
block to perform some tasks on completion without using await
then it'll simply run in background without blocking client-side. Using timeout for such scenario is not permissible because on a slow network it may take a minute to complete and once your timeout completed it'll will block client-side. Instead of using await
which will block your request remove it and it simply run asynchronously which I think you wanna achieve.
const myRequest = axios({
url: "https://jsonplaceholder.typicode.com/todos/1",
headers: {
accept: "application/json",
"Content-Type": "application/json"
},
})
Upvotes: 1