Prefer async/await for readability
Chain then() only when you are composing libraries. For app code, async functions with try/catch keep error paths visible.
Always decide what happens on failure: toast, inline error, retry, or silent fallback. “Undefined UI” is still a bug.
Cancel stale requests
When a user types fast or switches tabs, abort the previous fetch with AbortController. Otherwise older responses can overwrite newer state.
In React, clean up in useEffect return — or use a framework pattern that cancels for you.
Parallel vs sequential
Promise.all when requests are independent. Await in sequence when each step needs the previous result. Mixing them blindly is how spinners lie to users.