The most common implementation of a sleep function in javascript is returning a Promise after setTimeout resolves:
function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); }
I have for loop with await sleep to keep it from executing too fast, such as not requesting xhr too fast. I also have a isBreak flag elsewhere to tell me when to stop the for loop. However, the issue I have is that when I break the for loop, the previous await sleep has already executed and is holding up the for loop. Is there a better way of breaking the for loop and also terminating the await sleep instantaneously?
const items = []; let isBreak = false; // Somewhere else in the application for (const item of items) { if (isBreak) break; // Do something, like xhr request await sleep(15000); // 15 seconds sleep if (isBreak) break; }
Is there a way for me to signal for early
Advertisement
Answer
In JS, when an await
operation starts, it can no longer be interrupted; it will wait until its operand promise is settled.
So, you have to make the promise you’re await
ing cancelable in some way.
Unfortunately, your code can’t get notified about a variable reassignment (when you set isBreak
to true
), and polling it would be inefficient.
Instead of a flag, you could use an AbortSignal
(which was invented for this purpose), and make your sleep
accept one:
function sleep(ms, signal) { return new Promise((resolve, reject) => { signal.throwIfAborted(); const timeout = setTimeout(() => { resolve(); signal.removeEventListener('abort', abort); }, ms); const abort = () => { clearTimeout(timeout); reject(signal.reason); } signal.addEventListener('abort', abort); }); }
Then, you use it like this:
const items = []; const isBreak = new AbortController(); // Somewhere else in the application, call `isBreak.abort()` try { for (const item of items) { // Do something, like xhr request await sleep(15000, isBreak.signal); // 15 seconds sleep } } catch (e) { if (e.name === 'TimeoutError') { // Handle a cancellation console.log('Cancelled'); } else { // Not a cancellation, rethrow it throw e; } }
An AbortSignal
works well with fetch
as well, in case you have to cancel that too.