Ok – so I’ve written this and it works how I want it to, I just don’t understand how it works / why it’s working the way it does.
Can someone please explain what I’m missing here.
Here is the code:
const crypto = require('crypto');
class ProcessController extends AbortController {
timeouts = new Map();
sleep(ms) {
const id = crypto.randomBytes(8).toString('hex');
return new Promise((resolve) => {
this.timeouts.set(id, setTimeout(() => {
this.timeouts.delete(id);
resolve();
}, ms))
})
}
abort() {
super.abort();
for (const timeout of this.timeouts.values()) {
clearTimeout(timeout);
}
// not really necessary as not reusable but be tidy
this.timeouts.clear();
}
}
async function meLoop(controller) {
const { signal } = controller;
while (!signal.aborted) {
console.log("START OF LOOP. Uptime:", Math.floor(process.uptime()));
await controller.sleep(5 * 1000);
console.log('END OF LOOP');
}
console.log("BEYOND LOOP. Uptime:", process.uptime());
}
async function main() {
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const controller = new ProcessController();
meLoop(controller);
await sleep(23 * 1000);
controller.abort();
console.log("END. Uptime:", process.uptime());
}
main();
And here is the outcome:
> node testBreakSleep.js
START OF LOOP. Uptime: 0
END OF LOOP
START OF LOOP. Uptime: 5
END OF LOOP
START OF LOOP. Uptime: 10
END OF LOOP
START OF LOOP. Uptime: 15
END OF LOOP
START OF LOOP. Uptime: 20
END. Uptime: 23.044878139
Basically, it’s an abortable loop. My understanding was that during the time in the while loop, whatever is in there should run to completion before the while is evaluated again as Javascript is single threaded.
However, what I don’t get, is that when abort()
is called, and the setTimeout
s are cleared, it kind of just exits instead of finishing the loop? I don’t understand what happens to the promise? It doesn’t resolve, and the end of the loop is never reached (this is good for me)
Can someone explain?
Edit – thought I should mention, if I comment out the clearTimeout
in the abort()
function, Node hangs around until the promise resolves.
> node testBreakSleep.js
START OF LOOP. Uptime: 0
END OF LOOP
START OF LOOP. Uptime: 5
END OF LOOP
START OF LOOP. Uptime: 10
END OF LOOP
START OF LOOP. Uptime: 15
END OF LOOP
START OF LOOP. Uptime: 20
END. Uptime: 23.037329278
END OF LOOP
BEYOND LOOP. Uptime: 25.041987186
Thanks
2
Answers
When
meLoop()
callsthe function is suspended until the timeout in
sleep()
finishes and callsresolve()
.When you clear the timeout in
ProcessController.abort()
, it never callsresolve()
, so theawait
statement never completes and the loop hangs forever.Calling
await
blocks until the promise is resolved or rejected, so that means that callingcontroller.abort()
rigth after the loop has started won’t be effective until the promise has resolved and the loop starts over.Adding an event listener (if you’re working with nodejs you can use event emitters) to your cancellable async function might do the trick, maybe this example helps you to achieve what you want: