Awesome Reviewers

When multiple async operations can overlap with replacement/teardown (UI unmount, session rebuild, polling vs actions, stop/dispose, in-flight RPCs), require explicit ownership and lifecycle binding:

1) Claim ownership synchronously before the first await

2) Bind every async result to an identity token (generation / requestId / sessionId / tabId)

3) Serialize or coalesce work per scope

4) Cancellation-aware completion + deterministic cleanup

Code sketch (claim-before-await + generation + serial coalescing)

let pending: null | (() => void) = null;
let pendingToken = 0;
let lastApplied = 0;

async function resolvePendingWithAttachments(
  tokenAtCall: number,
  deliver: () => Promise<void>,
): Promise<boolean> {
  const resolve = pending;
  if (!resolve) return false;

  // Claim synchronously
  pending = null;
  const myToken = ++pendingToken;

  try {
    await deliver();
    // Bind to identity: only apply if still current
    if (myToken !== pendingToken) return true;
    lastApplied = tokenAtCall;
    resolve();
    return true;
  } catch {
    // Release the claim if delivery fails so retries can happen
    if (myToken === pendingToken) pending = resolve;
    throw;
  }
}

// Polling/action coalescing pattern
let inflight: Promise<void> | null = null;
let rerunRequested = false;

async function refresh(reason: string) {
  if (!enabled) return;
  if (inflight) { rerunRequested = true; return; }
  inflight = (async () => {
    do {
      rerunRequested = false;
      const currentReason = reason;
      const response = await fetchState(currentReason);
      apply(response); // only after request completes
    } while (rerunRequested);
  })();
  try { await inflight; } finally { inflight = null; }
}

Adopting this standard prevents: