Skip to content

add staleFetchTimeout option, and refetch after an aborted fetch rejects late - #415

Open
orgads wants to merge 2 commits into
isaacs:mainfrom
orgads:stale-fetch-timeout
Open

orgads wants to merge 2 commits into
isaacs:mainfrom
orgads:stale-fetch-timeout

Conversation

@orgads

@orgads orgads commented Oct 14, 2026 •

Copy link
Copy Markdown

Fixes #414.

Two commits:

  1. refetch after an aborted fetch rejects late. With allowStaleOnFetchAbort, a fetchMethod that rejects after the abort left its settled promise in the cache. Every later fetch() joined it, returned the stale value, and never called the fetchMethod again. Now a late rejection replaces the promise with the stale value, or deletes the entry, following noDeleteOnFetchRejection / allowStaleOnFetchRejection as a normal rejection would.
  2. add staleFetchTimeout option. When fetch() refreshes an entry that has a stale value and the fetchMethod hasn't settled within that many ms, fetch() resolves to the stale value and sets status.returnedStale and the new status.fetchTimedOut. The fetchMethod keeps running and updates the cache when it resolves, and fetch() calls joining it meanwhile get the stale value at once. If it rejects, the next fetch() calls the fetchMethod again (this uses the fix from commit 1). Entries with no stale value wait as usual, and the fetchMethod's signal is not aborted. It can be set on the constructor, per fetch() call, or from the fetchMethod.

Tests are in test/fetch.ts, at the end of the file so that fewer snapshot timestamps shift. The tracing.ts snapshots were regenerated; only start/now timestamps changed. Coverage stays at 100%, and lint and prettier are clean. I didn't touch the CHANGELOG or the version.

🤖 Generated with Claude Code

orgads and others added 2 commits October 14, 2026 12:41
With allowStaleOnFetchAbort, an aborted fetch resolves to the stale
value while the fetchMethod keeps running. A late resolution updates
the cache, but a late rejection was ignored: the settled promise
stayed in the cache, so every later fetch() joined it, returned the
stale value, and never called the fetchMethod again.

Replace the pending promise with the stale value (or delete the entry,
following the noDeleteOnFetchRejection settings) on a late rejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a fetch() refreshes an entry that has a stale value, and the
fetchMethod has not settled within staleFetchTimeout ms, resolve the
fetch() to the stale value. The fetchMethod keeps running and updates
the cache when it resolves; fetch() calls joining it meanwhile get the
stale value at once. Entries without a stale value wait as usual, and
the fetchMethod's AbortSignal is not aborted.

This covers "return stale on timeout while refreshing" without
creating a signal for every fetch() call, and without resolving to
undefined on a cold miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Option to return the stale value when a refresh is slow, without per-call signals

1 participant