Edit 2021 all platforms have converged on AbortController as the cancellation primitive and there is some built in support for this.
In Node.js
// import { setTimeout } from 'timers/promises' // in ESM
const { setTimeout } = require('timers/promises');
const ac = new AbortController();
// cancellable timeout
(async () => {
await setTimeout(1000, null, { signal: ac.signal });
})();
// abort the timeout, rejects with an ERR_ABORT
ac.abort();
In Browsers
You can polyfill this API and use the same as the example above:
function delay(ms, value, { signal } = {}) {
return new Promise((resolve, reject) => {
const listener = () => {
clearTimeout(timer);
reject(signal.reason);
};
signal?.throwIfAborted();
const timer = setTimeout(() => {
signal?.removeEventListener('abort', listener);
resolve(value);
}, ms);
signal?.addEventListener('abort', listener);
});
}
What you can do it that, you can return a canceller from your timeout
function and invoke it when needed. This way you do not need to store the timeoutid
globally (or on the outer scope) and also this can manage multiple calls to the function as well. Each instance of the object return by the function timeout
will have its own canceler that can perform the cancellation.
function timeout(ms) {
var timeout, promise;
promise = new Promise(function(resolve, reject) {
timeout = setTimeout(function() {
resolve('timeout done');
}, ms);
});
return {
promise:promise,
cancel:function(){clearTimeout(timeout );} //return a canceller as well
};
}
var timeOutObj =timeout(3000);
timeOutObj.promise.then(function(result) {
console.log(result); // timeout done
});
//Cancel it.
timeOutObj.cancel();
Plnkr