Skip to content

Instantly share code, notes, and snippets.

@eladkarako
Created May 14, 2026 21:17
Show Gist options
  • Select an option

  • Save eladkarako/e765b86fb448300a18aabd6042715ed5 to your computer and use it in GitHub Desktop.

Select an option

Save eladkarako/e765b86fb448300a18aabd6042715ed5 to your computer and use it in GitHub Desktop.
sleep - promise wrap around setTimeout, can be cancelled
/* sleep
* promise wrap around setTimeout.
* - can be cancelled (stops the timer and rejects the promise immediately).
* - can be set to execute method, with arguments, within a scope.
*
* ---------------------------------------------------------- simple case.
* (async ()=>{
* foo();
* await sleep(50 * 1000); //halts the code-block for 50 seconds.
* bar();
* })();
* ---------------------------------------------------------- a button that stops the timer and reject the promise right away (even when the async block is halted due to "wait sleep"). you can re-assign the promise, so the button will be able to cancel whatever sleep you set. this is also a very nice way to reject any promise...
* let promise = undefined;
* const button = document.createElement("button");
* const click_handler = (ev)=>{
* if("function" === typeof promise && "function" === typeof promise.cancel_immediately){ //might not be available yet.
* promise.cancel_immediately();
}
* };
* button.addEventListener("click", click_handler, {capture:false, passive:true, once:false});
* document.querySelector("body").appendChild(button);
*
* (async ()=>{
* promise = sleep(50 * 1000); //returns a promise right away. and populate a variable that is used by the button click.
* await promise(); //halts the async block. but if the user will click the button, it will immediately clear the timeout and reject it.
* })();
* ----------------------------------------------------------
*/
const sleep = async (milliseconds, optional_fn, optional_arguments, optional_scope)=>{ "use strict";
milliseconds = milliseconds || 0;
let is_settled = false;
let timer_id = undefined; //exposed from within the promise.
let promise_reject = undefined; //exposed from within the promise.
const promise = new Promise((resolve, reject)=>{
promise_reject = reject; //expose, to be able to use it when canceling the timer. which itself is exposed as piggyback on top of the returned promise.
timer_id = setTimeout(()=>{
if(true === is_settled){ return; } //prevent double-settlement. regardless if resolved or rejected.
is_settled = true;
optional_arguments = optional_arguments || [];
optional_scope = optional_scope || this;
let result = undefined;
if("function" === typeof optional_fn){
result = optional_fn.apply(optional_scope, optional_arguments);
}
resolve(result); //even if the potential function was not returning a result.
}, milliseconds);
});
promise.cancel_immediately = ()=>{ //cancel timer and reject promise without waiting for the rest of the time. it does not execute any optional method.
is_settled = true;
clearTimeout(timer_id);
timer_id = null;
const error = "undefined" !== typeof DOMException ? new DOMException("sleep cancelled.","AbortError") : (()=>{ const e = new Error("sleep cancelled."); e.name = "AbortError"; return e; })();
if("function" === typeof promise_reject){
promise_reject(error);
}
};
return promise;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment