Is there a version of setTimeout that returns an ES6 promise?
Solution 1
In Browsers
First of all no - there is no built in for this. Lots of libraries that enhance ES2015 promises like bluebird whip with it.
I think the other answer conflates executing the function and a delay, it also creates timeouts that are impossible to cancel. I'd write it simply as:
function delay(ms){
var ctr, rej, p = new Promise(function (resolve, reject) {
ctr = setTimeout(resolve, ms);
rej = reject;
});
p.cancel = function(){ clearTimeout(ctr); rej(Error("Cancelled"))};
return p;
}
Then you can do:
delay(1000).then(/* ... do whatever */);
Or
doSomething().then(function(){ return delay(1000); }).then(doSomethingElse);
If we only want the basic functionality in ES2015, it's even simpler as:
let delay = ms => new Promise(r => setTimeout(r, ms));
In Node
You can use util.promisify
on setTimeout
to get a delay
function back - meaning you don't have to use the new Promise
constructor anymore.
Solution 2
Here's how I'd implement it:
function delay(duration, func) {
var args = Array.prototype.slice.call(arguments, 2);
return new Promise(function (resolve) {
setTimeout(function () {
resolve(func.apply(null, args));
}, duration);
});
}
(ES5-syntax intentionally chosen)
But maybe there's a common library that already does this, or a better way to do it.
Solution 3
If you need the proper cancellation of promised timeout similar to clearTimeout
- returning the promise directly from setTimeout
is not convenient. Especially when using with ES7 async / await in try...finally
block. It is better to have separate variable for timeout manipulation. I've implemented this approach as tiny await-timeout package. It works as following:
import Timeout from 'await-timeout';
async function foo() {
const timeout = new Timeout();
try {
const fetchPromise = fetch('https://example.com');
const timerPromise = timeout.set(1000).then(() => console.log('Timeout!'));
await Promise.race([fetchPromise, timerPromise]);
} finally {
timeout.clear();
}
}
In this example timeout will definitely be cleared in case of fetch success or any error and console.log('Timeout!')
will not be called.
Michael Kropat
Projects I've published that may interest you: BetterWin32Errors — a better interface to winerror.h dapper-invoice — hours invoice featuring style over substance Is Shell Command ______ Portable? — reference website jumpapp — run-or-raise application switcher for X11 desktops MlkPwgen — secure password generator (.NET + PowerShell) secure-random-password — password generator (JavaScript) sh-realpath — a portable, pure shell implementation of realpath SSLfie — generate self-signed x.509 certificates for SSL/TLS
Updated on June 02, 2022Comments
-
Michael Kropat almost 2 years
Similar to this question, but rather than asking about how promises work in general, I specifically want to know:
What is the standard/best way to wrap setTimeout in something that returns a Promise? I'm thinking something like Angular's
$timeout
function, but not Angular specific. -
freakish over 8 yearsThis looks fine. It's such a short piece of code that I don't really see a point in digging through libraries to find something similar.
-
Michael Kropat over 8 yearsBeautiful answer. I'm totally glad I asked the question now.
-
Bergi over 8 yearsDon't do that. You should promisify at the lowest level -
setTimeout
- and move thefunc
in a promise callback, where exceptions from it will be properly caught. -
vitaly-t over 8 yearsProblem with this example:
clearTimeout
should be called when resolving also. -
Timothy Vann over 7 yearswhen I try either version of @Benjamin Gruenbaum's answer above, the code following then( executes immediately without waiting for the delay. Example
delay(2000).then(console.log("then 1")).then(delay(2000).then(console.log("then 2")));
prints both "then 1" "then 2" immediately with no delay, two seconds later the program finishes. -
Benjamin Gruenbaum over 7 years@TimothyVann
then
takes a function - not a promise. -
Bergi over 6 yearsThis makes no sense. You're only clearing the timeout after having it awaited already…
-
vitalets over 6 yearsFixed. Thank you!