canceling the results of a promise
By Per Fröjd
- snippets
- react
- javascript
Dealing with long-running requests
In some of the React applications I’ve dealt with, I’ve come across long-running http requests, often tied tightly to the local state of a component. In some cases, these requests are to external APIs, and are often initiated after the component has mounted and simply updates a small part of the UI once the request has been finished.
Normally, most operations should be cancelled on the lifecycle event of componentWillUnmount, but what if the request is still running by the time the component is being unmounted. In most cases, you’ll end up with a warning in the console regarding setting the state of an unmounted component, which isn’t the end of the world, but annoyed me enough to find a solution to the problem.
Wrapping the promises
NOTE: Original credit to the snippet should go to jimfb@github and alangpierce@github.com, found at the following issue.
const makeCancelable = (promise) => {
let hasCanceled = false
const wrappedPromise = new Promise((resolve, reject) => {
promise
.then((val) =>
hasCanceled ? reject({ isCanceled: true }) : resolve(val)
)
.catch((error) =>
hasCanceled ? reject({ isCanceled: true }) : reject(error)
)
})
return {
promise: wrappedPromise,
cancel() {
hasCanceled = true
},
}
}
Usage
// Wrap the promise
const longRunningPromise = makeCancelable(ExternalAPI.veryHeavyRequest());
// Execute it (more or less identical to how it would be done otherwise)
longRunningPromise
.promise
.then((returningData) => {
this.setState({
data: returningData
});
})
.catch({ hasCanceled, ...error }) => {
if (hasCanceled) {
// Promise has been canceled and shouldn't trigger any ordinary error-handling
return;
}
// Normal error-handling, notify the user etc..
});
// Now simply call the following .cancel() method.
longRunningPromise.cancel();
Final notes
This solution is definitely not the end-all, and there’s a bunch of different solutions found in the issue thread found here. This has worked without issue for me until something better comes along.