Async, explained properly
The concept behind a large share of your bugs.
If you've hit "cannot read properties of undefined" on data that definitely exists, or a loading spinner that never stops, you've hit async. It's worth understanding once, properly.
The idea
Some things take time — a network request, reading a file, a database query. Rather than freezing everything while waiting, the language starts the operation, carries on, and comes back when the result arrives.
Which means: the line after your request runs before the result exists.
const data = fetchData(); // starts the request console.log(data); // runs NOW, data isn't here yet
That's the whole source of the confusion, and every async feature exists to manage it.
What a promise is
An object representing "a value that will exist later". It's pending, then it either resolves with a value or rejects with an error.
await means "pause this function until the promise settles, then continue with the value". It doesn't block the whole program — just that function.
The rules that prevent most bugs
Every await needs a try/catch, or the error vanishes into an unhandled rejection you never see.
A function containing await is itself async, and returns a promise. Callers must await it too. Forgetting this one level up is extremely common.
Don't await inside a loop when the operations are independent. Ten sequential requests take ten times as long as ten parallel ones. Promise.all runs them together.
Do await sequentially when each depends on the last. Parallelising dependent operations produces race conditions.
Never mix await with a callback API without wrapping the callback in a promise first.
Review this for async problems: <paste> Check specifically: - Any promise not awaited, or awaited without error handling. - Any async function whose caller doesn't await it. - Sequential awaits in a loop that could run in parallel. - Parallel operations that actually depend on each other. - State updated after the thing that owns it has gone away. For each: what actually goes wrong, and when it would show up.
"Works locally, fails in production" is often async. Your local database answers in a millisecond; the production one takes 200ms, and the race condition you always had starts happening.