← All notes

Who owns an asynchronous response?

Separate search ordering, identity changes and page disposal. Check stale success, failure and finally paths.

Basis of this note

Based on public useRequestScope/useGallery code and the five deferred-response checks rerun on 29 October. The code example below is a compact teaching sketch.

A response can outlive its owner

Consider opening a private album as account A, signing out, then receiving the album response. The response may be valid for the original request while being inappropriate for the current screen. The same problem appears when an order finishes after an account switch or a location request completes after selecting another activity.

Debouncing controls how often work starts. It does not decide whether a result still belongs to the current identity or selection. That decision needs a context check at the point where state is written.

Invalidate the context explicitly

useRequestScope captures a generation and returns a validity function. Its unmount hook invalidates the generation. useGallery additionally watches user ID, role, status and management mode with synchronous flushing, clears old data and starts a new load. The caller is responsible for wiring these invalidation events.

JavaScript
// Teaching sketch: call invalidate on identity or page changes.
let epoch = 0;
const invalidate = () => { epoch += 1; };
async function load(read) {
  const mine = epoch;
  state.loading = true;
  try {
    const result = await read();
    if (mine === epoch) state.data = result;
  } catch (error) {
    if (mine === epoch) state.error = error.message;
  } finally {
    if (mine === epoch) state.loading = false;
  }
}

Identity scope and latest request are different

Two requests may belong to the same account but different search terms. They need a request sequence as well as an identity epoch. An old catch can replace a newer result with an error; an old finally can remove the loading indicator from a newer request. Guard every state write, not only the successful result.

There is a follow-up in the reviewed gallery loader: success checks both request generation and scope, while catch checks only scope. The five existing scenarios do not test a stale list failure within one identity. That is a proposed regression case, not a verified fix.

Discarding a result does not undo a write

AbortController can stop some client work, but the server may already have committed a mutation. A frontend epoch prevents stale rendering; retry safety still requires server authorization, an idempotency strategy and an operation-status query when the outcome is uncertain.

The five passing fixtures cover logout during album loading, album save, account switching during order submission, activity switching during location acquisition and logout during posting. They check UI state isolation with deferred promises and do not simulate real database transactions.

Sources & further reading

Compiled from code and documents, with AI-assisted drafting and translation. Examples and proposed checks are learning material; only linked evidence establishes what was run.