skip to content
usubeni fantasy logo Usubeni Fantasy

How do I cancel Ajax? Should I cancel it or not?

/ 5 min read

This Post is Available In: CN EN ES JA

Ajax cancel

If you are familiar with xhr, you would know that Ajax can actually be canceled by the frontend using XMLHttpRequest.abort(). Of course, it’s not the Stone Age anymore, and apart from interviews, you probably won’t be hand-writing xhr anymore. In the well-known axios, there are two ways to cancel requests:

First is the old-fashioned cancelToken:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios
.get("/user/12345", {
cancelToken: source.token,
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log("Request canceled", thrown.message);
} else {
// handle error
}
});
axios.post(
"/user/12345",
{
name: "new name",
},
{
cancelToken: source.token,
},
);
// cancel the request (the message parameter is optional)
source.cancel("Operation canceled by the user.");

Then there’s the new thing (which is not really new) called AbortController:

const controller = new AbortController();
axios
.get("/foo/bar", {
signal: controller.signal,
})
.then(function (response) {
//...
});
// cancel the request
controller.abort();

After passing the cancelToken and signal to axios, both will call XMLHttpRequest.abort() using some mechanism.

onCanceled = (cancel) => {
if (!request) {
return;
}
reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
request.abort();
request = null;
};
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted ? onCanceled() : config.signal.addEventListener("abort", onCanceled);
}

cancelToken uses the publish-subscribe pattern to notify axios to cancel the request. Although this part is implemented by axios itself, it originates from a tc39 proposal called cancelable promises proposal, which has been abandoned.

On the other hand, AbortController is an interface that can already be used in browsers. As the name suggests, it is a controller specifically designed for aborting actions. The example used in mdn is also an Ajax request, but it uses the trendy and up-to-date fetch. From this, we can see that axios and fetch have consistent practices.

function fetchVideo() {
controller = new AbortController(); // Create a new controller
const signal = controller.signal;
fetch(url, { signal }) // Pass the signal to the fetch method
.then(function (response) {
console.log("Download complete", response);
})
.catch(function (e) {
console.log("Download error: " + e.message);
});
}
abortBtn.addEventListener("click", function () {
if (controller) controller.abort(); // Call controller.abort to cancel the fetch
console.log("Download aborted");
});

Other uses of AbortController

Of course, AbortController is not only used to abort Ajax requests. By looking at the DOM specification document, we can see two other usage examples:

One practical example is to use AbortController to cancel event listeners:

dictionary AddEventListenerOptions : EventListenerOptions {
boolean passive = false;
boolean once = false;
AbortSignal signal;
};

By passing signal to AddEventListener, you can cancel event listeners by calling abort(). This method is especially useful for anonymous callback functions.

Another example is to use it to abort promises. This is a concise and self-documented method… However, in fact, you don’t necessarily need AbortController to achieve this. You just need to find a way to access the reject of the promise. I think the focus of this example is more on learning how to use the onabort event of the signal:

const controller = new AbortController();
const signal = controller.signal;
startSpinner();
doAmazingness({ ..., signal })
.then(result => ...)
.catch(err => {
if (err.name == 'AbortError') return;
showUserErrorMessage();
})
.then(() => stopSpinner());
// ...
controller.abort();
function doAmazingness({signal}) {
return new Promise((resolve, reject) => {
signal.throwIfAborted();
// Begin doing amazingness, and call resolve(result) when done.
// But also, watch for signals:
signal.addEventListener('abort', () => {
// Stop doing amazingness, and:
reject(signal.reason);
});
});
}

In summary, the signal is a simple transmitter that is primarily used to cancel certain operations. If you don’t want to implement a pubsub object in certain situations, you can use this.

Let’s wrap up the introduction to AbortController. I wonder if everyone has gradually forgotten the title… Finally, let’s discuss whether canceling Ajax is really useful.

To cancel or not to cancel, that is the question

In fact, this Ajax cancellation is just a self-talk of the front-end. The back-end doesn’t know that it needs to be aborted, so the request that has been sent will still be executed. If the back-end doesn’t handle it specially, even if you cancel a 10s request, the back-end will still be struggling to process it.

So, is the so-called “optimization” mentioned in some articles, which suggests “canceling requests and keeping only the last one”, really meaningful?

Let’s discuss it based on different situations. For requests like POST that modify data, even if each request takes a long time to return, the server is already processing it. Canceling the previous POST and sending another one is undoubtedly a foolish behavior.

As for GET requests, and only for certain extreme operations, there may be some effect. For example, when retrieving an extremely long table and the result is not obtained, the user quickly returns a small amount of data through a search and renders it. When the extremely long table is finally returned, it will overwrite the data from the search. In this case, canceling is indeed effective. There are also cancellations for downloads and uploads, but they are probably rarely used.

Finally, let’s talk about a reasonable but practically useless benefit: after canceling, it can save a request slot. After all, the number of simultaneous requests for a domain in a browser is limited. In most cases, timeouts are more practical than canceling. Well… unless there are five or six extremely slow requests lined up at the same time, the rotation is still relatively fast…

My personal suggestion is that, in the end, this so-called “cancellation” is a special handling for extremely special situations. It’s good to know about it, but there is no need to cancel requests in the interceptor for no reason.

References

评论组件加载中……