Skip to content

Commit fef7fba

Browse files
justin808claude
andcommitted
Add whiteboard analogy to promise cleanup comment
Makes the subtle timing bug more approachable before diving into the technical explanation. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent f585f1c commit fef7fba

1 file changed

Lines changed: 14 additions & 11 deletions

File tree

  • packages/react-on-rails-pro-node-renderer/src/worker

packages/react-on-rails-pro-node-renderer/src/worker/vm.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -350,18 +350,21 @@ export async function buildVM(filePath: string) {
350350

351351
// Clean up the map entry after the promise settles (fulfills or rejects).
352352
//
353-
// Why .finally() here instead of try/finally inside the async IIFE:
353+
// Analogy: We write jobs on a whiteboard so nobody starts duplicates. If we
354+
// told the helper "erase it when you're done" but the helper failed so fast
355+
// they erased it *before we wrote it down*, the failed job would be stuck on
356+
// the whiteboard forever — blocking all retries. Instead, we attach a sticky
357+
// note to the job saying "erase me when I'm done." The note can't activate
358+
// until after we've written the job down, so erasing always happens in the
359+
// right order.
354360
//
355-
// An async IIFE executes synchronously until its first `await`. If the code
356-
// throws before reaching any `await` (e.g. readFileAsync rejects for a
357-
// missing file), the IIFE's internal finally block runs *synchronously* —
358-
// before `vmCreationPromises.set()` on line 349 has executed. That means
359-
// set() stores an already-rejected promise that never gets cleaned up,
360-
// permanently poisoning retries for this filePath.
361-
//
362-
// By chaining .finally() on the promise *after* set(), we guarantee the
363-
// delete() runs as a microtask after the current synchronous execution
364-
// completes — so set() has always run first, and the stale entry is removed.
361+
// Technically: an async IIFE runs synchronously until its first `await`. If
362+
// it throws before any `await` (e.g. missing file), a try/finally inside the
363+
// IIFE would run *before* set() on line 349 stores the promise — leaving a
364+
// stale rejected promise in the map that permanently poisons retries.
365+
// Chaining .finally() on the promise *after* set() guarantees cleanup runs
366+
// as a microtask after the current synchronous execution, so set() has
367+
// always run first.
365368
void vmCreationPromise
366369
.catch(() => {})
367370
.finally(() => {

0 commit comments

Comments
 (0)