Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 2x 5905x 5905x 5905x 5905x 5905x 5905x 5905x 5905x 5905x 5905x 5905x | /**
* The one asynchronous primitive this codebase needs, and the reason it is not
* a one-liner at its call site.
*/
/**
* Hand the event loop back, so a message already queued for this thread is
* actually delivered before the caller continues.
*
* **`await Promise.resolve()` does not do this.** It schedules a *microtask*,
* and the microtask queue is drained inside the task that scheduled it —
* including microtasks queued while draining. A loop that yielded that way runs
* to completion inside a single task, so nothing else on the thread ever gets a
* turn.
*
* That is not academic here. The full pixel scan behind the whole-metagenome
* overview (spec §7.3) yielded with `Promise.resolve()` between chunks and was
* therefore **uncancellable**: the `cancel` message sat in the worker's queue
* untouched until the scan had already finished, every other request queued
* behind it, and the completed overview was posted to a user who had asked for
* it to stop.
*
* A `MessageChannel` round trip is a real task. It is preferred over
* `setTimeout(0)` because nested timeouts are clamped to 4 ms after the fifth
* level, which over a thousand chunks would add seconds to a scan; a channel
* message costs microseconds. `setTimeout` remains the fallback for runtimes
* with no `MessageChannel`.
*/
export function yieldToEventLoop(): Promise<void> {
if (typeof MessageChannel === 'undefined') {
return new Promise((resolve) => {
setTimeout(resolve, 0)
})
}
return new Promise((resolve) => {
const channel = new MessageChannel()
channel.port1.onmessage = (): void => {
channel.port1.close()
channel.port2.close()
resolve()
}
channel.port2.postMessage(undefined)
})
}
|