JavaScript developers have all faced it: the frustration of wanting to cancel a long-running task only to find that the existing promise API doesn’t support this. If you’ve felt that frustration, you’re not alone. This article introduces two powerful methods to ease that frustration.
1. Using the New Promise.withResolvers()
The newly introduced `Promise.withResolvers()` is a powerful tool that allows you to manage promises in JavaScript more flexibly. In simple terms, this API returns an object containing two functions that can either resolve or reject a promise. Now, you can easily cancel promises without writing complex logic.
Here’s how you can use `Promise.withResolvers()`:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
Now, you can write it more concisely like this:
const { promise, resolve, reject } = Promise.withResolvers();
With this method, you can easily write code to run and cancel tasks as needed. Imagine how useful this code will be when you’re running a long task and don’t need to wait for it to finish anymore!
const buildCancelableTask = <T>(asyncFn: () => Promise<T>) => {
let rejected = false;
const { promise, resolve, reject } = Promise.withResolvers<T>();
return {
run: () => {
if (!rejected) {
asyncFn().then(resolve, reject);
}
return promise;
},
cancel: () => {
rejected = true;
reject(new Error("CanceledError"));
},
};
};
The above code clearly implements a cancellation mechanism. In the example where a task is set to complete after 1000ms, if you cancel it after 500ms, the task will be stopped, and an error will be returned.
2. Using AbortController
You’re probably already familiar with using `AbortController` to cancel requests. What if you could apply this familiar feature to promise tasks as well? That’s exactly what the second method introduces.
Using `AbortController` to cancel promises is also very straightforward:
const buildCancelableTask = <T>(asyncFn: () => Promise<T>) => {
const abortController = new AbortController();
return {
run: () =>
new Promise()<T>((resolve, reject) => {
const cancelTask = () => reject(new Error("CanceledError"));
if (abortController.signal.aborted) {
cancelTask();
return;
}
asyncFn().then(resolve, reject);
abortController.signal.addEventListener("abort", cancelTask);
}),
cancel: () => {
abortController.abort();
},
};
};
The advantage of this method is that even if you attempt to cancel multiple times, the task will only be canceled once. This is particularly useful in complex user interfaces where you need to cancel a previous task and start a new one.
Conclusion: Managing Promises Has Never Been Easier
By using the two methods introduced above, you no longer have to waste time waiting for long and tedious tasks to finish. Now you can manage promises in JavaScript more effectively. Try applying these new features to your projects and experience how much more convenient your development work becomes.
Reference: Web Developer, “How to Annul Promises in JavaScript”