Explain Promise failure propagation
Explain then/catch/finally behavior across async boundaries. Then apply it to a realistic product screen where a user action, browser behavior, and rendering timing all matter.
Answer Strategy
For promise failure propagation, do not answer like a glossary entry. State the rule, show where it appears in product UI, then name the user-visible bug that happens when the rule is misunderstood.
A strong foundation answer has three layers: the browser or language model, a tiny code example, and a frontend consequence such as stale state, broken focus, blocked input, unsafe data, or flaky tests.
The reference example below is intentionally small but production-shaped: it names the boundary, protects the failure mode, and includes a test that proves the rule instead of relying on explanation alone.
Reference Example: Promise Failure Boundary
Use a result union so async failures are explicit and the UI cannot accidentally render partial success as truth.
type LoadResult<T> =
| { ok: true; data: T }
| { ok: false; message: string; retryable: boolean };
async function loadJson<T>(url: string, signal?: AbortSignal): Promise<LoadResult<T>> {
try {
const response = await fetch(url, { signal, headers: { accept: 'application/json' } });
if (!response.ok) {
return { ok: false, message: 'HTTP ' + response.status, retryable: response.status >= 500 };
}
return { ok: true, data: (await response.json()) as T };
} catch (error) {
if (signal?.aborted) {
return { ok: false, message: 'Request cancelled', retryable: false };
}
return {
ok: false,
message: error instanceof Error ? error.message : 'Unknown failure',
retryable: true,
};
}
}Testing Strategy
Convert the answer into observable behavior. In a mid-senior interview, say which behaviors are covered by unit tests, interaction tests, accessibility checks, and one browser smoke path.
test('loadJson turns rejected promises into explicit UI state', async () => {
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new Error('network down'))));
await expect(loadJson<{ id: string }>('/api/user')).resolves.toEqual({
ok: false,
message: 'network down',
retryable: true,
});
});Interviewer Signal
Shows whether you understand promise failure propagation as an operating model, not as memorized trivia.
Constraints
- Use one concrete browser or React-facing example.
- Name the failure mode a production user would notice.
- Keep the first answer under two minutes before expanding.
Model Answer Shape
- Start with the rule: then/catch/finally behavior across async boundaries.
- Tie the rule to ownership: what runs in render, what runs after paint, what is external state, and what must be cleaned up.
- Close with the smallest test, trace, or code review check that would catch the bug.
Tradeoffs
- A short interview answer is easier to follow, but a senior answer must still name the edge case.
- Framework vocabulary helps only after the browser or language rule is clear.
Edge Cases
- Slow devices where timing bugs become visible.
- Repeated user actions before async work settles.
- Browser defaults that differ from custom component behavior.
Testing And Proof
- Unit-test the pure decision when possible.
- Use an interaction test for focus, keyboard, timing, or cleanup behavior.
Follow-Ups
- How would this change in a React component?
- What would you log or profile if this broke in production?