Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel timeout inside of Javascript Promise?

Tags:

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

like image 419
spirytus Avatar asked Aug 17 '14 02:08

spirytus


People also ask

Can you cancel a promise JavaScript?

To force cancel a promise with JavaScript, we use the AbortController constructor. const controller = new AbortController(); const task = new Promise((resolve, reject) => { //... controller.

Can we stop setTimeout in JavaScript?

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.

Can I clear timeout inside setTimeout?

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.

How do you reject a promise after timeout?

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.


1 Answers

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(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

like image 79
PSL Avatar answered Sep 19 '22 13:09

PSL