I see from this question that it can be an antipattern to mix Promises with async code.
Does this, however, apply in all cases?
I can't see an easy way to avoid combining them in the following code:
- It's an alternative to
setIntervalwhich waits for an invocation to complete before scheduling the next one - However, unlike recursive
setTimeout, it does not constitute an open memory leak in browsers that don't yet support TCO
Does this code embody an antipattern? And, if so, how can I remedy it without introducing a memory leak?
See, in particular, line 10: new Promise( async (resolve) => {
—this seems very non-idiomatic, but I don't see another way to accomplish: wrapping an await statement in a while loop per se, dispatching it, and returning a handle to abort the loop.
var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const repeatedTimeoutIntervals = [];
function setRepeatedTimeout(f, delay, ...arguments) {
//Like setInterval, but waits for an invocation to complete before scheduling the next one
//(Supports both classic and async functions)
const mySemaphores = {notAborted: true};
const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
new Promise( async (resolve) => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments);
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
});
return intervalID;
}
function clearRepeatedTimeout(intervalID) {
//Clears loops set by setInterval()
repeatedTimeoutIntervals[intervalID].notAborted = false;
}
return [setRepeatedTimeout, clearRepeatedTimeout];
})();
<p><button onclick="(function createInterval(){
const _ = {intervalID: undefined};
_.intervalID = setRepeatedTimeout( () => {
console.log(`Hello from intervalID ${_.intervalID}`)
}, 2000)
})()">Create timer</button><br />
<form action="javascript:void(0);" onsubmit="(function clearInterval(intervalID){
clearRepeatedTimeout(intervalID);
})(parseInt(event.target.elements.intervalID.value))">
<input name="intervalID" placeholder="intervalID"/><button type="submit">Clear timer</button></p>
The problem that the other question was warning about, and that could be a problem here, is that if the inside of the async callback passed to the Promise constructor awaits something that rejects, the Promise will hang instead of rejecting. Your current code will not result in
fever rejecting, butsetRepeatedTimeoutwere to carry out a task which may reject, you'd get an unhandled rejection and permanent hanging:If you want the loop to continue when such an error is encountered, there's a way to handle such problems while keeping the
async: just catch everything that might reject (either in atry/catchor with.catch):But there's really no need for the
new Promisehere at all - it never resolves, and never gets used. Just use an async IIFE: