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