Promises
In short: a Promise is a claim ticket for a value that isn't ready
yet. It starts pending, and settles exactly once — either
fulfilled, with a value (.then handles this), or rejected, with
a reason (.catch handles this). .finally runs afterwards no matter
which happened, the same way finally always runs after try/catch.
Once settled, a Promise's outcome is locked in forever; it can never
change or settle a second time.

Waiting for stock to be checked
Wrapping an async operation in a Promise gives it a clean, single result
to react to, instead of a callback:
checkStock returns a Promise immediately — pending. Once the simulated
check finishes, it calls resolve with the stock message, which settles
the Promise as fulfilled. await pauses here until that happens, then
gives you the resolved value directly.
then, catch, and finally, all in one chain
The three handlers cover fulfilled, rejected, and "either way":
checkStock("sold-out-item") rejects, so .then is skipped entirely —
only .catch runs, receiving the Error. .finally runs after either
outcome, exactly like a finally block: the right place for cleanup that
must happen whether the operation succeeded or failed.
Running several checks at once
Promise.all waits for every Promise in an array to fulfill, and gives
you their results in the same order — a clean way to run independent
async work in parallel instead of one after another:
All three checks run at the same time, not one after another — the
total wait is roughly one check's worth of time, not three. If any one of
them had rejected, Promise.all itself would reject immediately with that
reason, skipping the rest.
Quick practice
What are the three possible states of a Promise?
If a Promise rejects, which handler runs?
Why use Promise.all instead of awaiting three checkStock calls one after another with separate await lines?
