I'm toying with promises in JavaScript and tried to promisify setTimeout function:
function timeout(ms) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('timeout done'); }, ms); }); } var myPromise=timeout(3000); myPromise.then(function(result) { console.log(result); // timeout done })
Fairly straightforward but I was wondering how would I go about canceling my timeout before the promise resolves. timeout
returns Promise
object hence I loose access to value that setTimeout
returns and cannot cancel timeout via clearTimeout
. What woud be the best way to do it?
BTW there is no real purpose for this, I just wonder how this would be approached. Also I plunked it here http://plnkr.co/edit/NXFjs1dXWVFNEOeCV1BA?p=preview
To force cancel a promise with JavaScript, we use the AbortController constructor. const controller = new AbortController(); const task = new Promise((resolve, reject) => { //... controller.
To cancel a setTimeout() method from running, you need to use the clearTimeout() method, passing the ID value returned when you call the setTimeout() method.
clearTimeout() inside setTimeout() method not working in JS It does "work", but your logic is incorrect. After you called clearTimeout you are calling setTimeout again. Instead of calling clearTimeout you should just exit the function.
const timeout = (prom, time) => Promise. race([prom, new Promise((_r, rej) => setTimeout(rej, time))]); With this helper function, wrap any Promise and it will reject if it does not produce a result in the specified time.
Edit 2021 all platforms have converged on AbortController as the cancellation primitive and there is some built in support for this.
// 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();
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(new Error('Aborted')); }; const timer = setTimeout(() => { signal?.removeEventListener('abort', listener); resolve(value); }, ms); if (signal?.aborted) { listener(); } 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With