Callbacks
A callback is a function you hand to other code, which calls it later. setTimeout(callback, 500) asks the timer to call it after at least 500 ms. Meanwhile your code keeps running; nothing stops to wait.
The script runs
The engine runs the script top to bottom. Lines 1 to 5 only define the function wait; nothing inside it runs yet. Line 6 prints start to the console.
Line 7 calls wait(500), which jumps to line 2. new Promise makes a promise: an object that stands for a value that is not ready yet. It starts pending. The function passed to new Promise runs at once.
Line 3 hands a callback to setTimeout. The timer counts 500 ms outside the engine. wait returns the pending promise p1 straight away: the script has not waited at all.
.then(callback) stores a callback on a promise and returns a new promise. Line 8 stores one on p1 and returns p2; line 9 stores one on p2 and returns p3. None of these callbacks has run yet.
Line 10 prints end. The script has finished and the engine has nothing left to run, yet got has not printed. The code after wait(500) did not wait for it.
Later: the promise settles
At least 500 ms later, the timer's callback runs as a new task. It calls resolve(500), so p1 is fulfilled with the value 500. A promise settles only once: it never changes state or value again.
Fulfilling p1 does not call line 8's callback directly. It puts the callback, with the value 500, in the microtask queue. When the current task ends, the event loop runs every microtask before the next task.
Line 8's callback runs with ms = 500 and returns 1000. That return value fulfils p2 with 1000, so p2's callback joins the queue too. Each link of the chain hands its result to the next.
Line 9's callback runs with n = 1000 and prints got 1000. console.log returns undefined, so p3 is fulfilled with undefined. The console reads start, end, got 1000.
When it fails
If the work fails, calling reject(error) instead of resolve rejects the promise. A rejection skips the .then callbacks down the chain until a .catch(callback) handles it. The chain stays flat, unlike callbacks nested in callbacks.
What happens
// step 1/11 · callback
callback a function passed to other code,
to be called later
line 3 setTimeout(() => resolve(ms), ms)
the arrow function is the callback
the timer calls it after >= 500 ms
JavaScript never pauses to wait for it
The ECMAScript specification calls a queued promise callback a job (PromiseReactionJob); browsers and Node.js run these jobs as microtasks. After every task, the event loop runs microtasks until the queue is empty, including ones added meanwhile, which is why p2's callback runs before any other task. p1, p2 and p3 are the figure's names. The script never stores them in variables: wait(500) returns p1, and each .then call returns the next one.