TL;DR: It appears Promise's resolve()
returns, which in a looping function causes the promise to keep running. What's the proper way to call resolve() for looping promises?
Details:
I built a setTimeOut loop for animation purposes which would run for a number of times, then exit the loop.
Simply calling resolve
when done did not work: it did resolve the promise but then kept running.
function timeoutLoop (fcn, steps = -1, time = 0) {
return new Promise((resolve) => {
function timeoutStep () {
if (steps-- === 0) resolve() // steps will be -1
fcn()
setTimeout(timeoutStep, time)
console.log(steps)
}
timeoutStep()
})
}
timeoutLoop(() => {}, 10).then(() => console.log('done'))
Two fixes worked: use an if-else
or have the if use return resolve()
Use if-else
if (steps-- === 0) {
resolve() // steps will be -1
} else {
fcn()
setTimeout(timeoutStep, time)
console.log(steps)
}
return the resolve() call
if (steps-- === 0) return resolve() // steps will be -1
My concern is that, even if both "work", there may be side effects I don't understand such as the call-stack retaining the call frame of the Promise body.
What's the best practice in this case?
Edit: The answer @Bergi gave, using async functions, is clearly the right philosophy. I did make one minor change, using a while loop rather than recursion:
function timeoutPromise( ms = 1000) {
return new Promise(resolve => { setTimeout(resolve, ms) })
}
async function timeoutLoop (fcn, steps = -1, time = 0) {
while (steps-- !== 0) { // Note decr occurs *after* comparison
fcn()
await timeoutPromise(time)
}
}
timeoutLoop(() => {}, 10, 1000).then(() => console.log('done'))