Promises aren't being processed directly, but are based on a queue. If the promise completes, the callback is processed in the next processing cycle.
If we were to replace your code with a more verbose one, we would get:
const a = Promise.resolve('a');
const b = a.then(() => {
console.log('b');
const c = Promise.resolve('c');
return c;
});
const ca = b.then((res) => {
console.log('ca', res)
})
const u = Promise.resolve('u');
const v = u.then(() => {
console.log('v');
});
const w = v.then(() => {
console.log('w');
});
const x = w.then(() => {
console.log('x');
});
const y = x.then(() => {
console.log('y');
});
const z = y.then(() => {
console.log('z');
});
First pass the code is running top to bottom and not a lot is going on. Two promises are being put in the queue as resolved (a and u) and nothing is printed. When those are being processed in the next cycle, they queue both b and v.
Whenever the next cycle is being processed, b is first up, logs "b" and queues the c. After that, v is processed, logs "v" and queues w.
Now, c is done, and queues the next ca (indirection, because of returning a promise in a promise), but doesn't print anything. And so on.
All the way down, it would looks something like this:
// Main tick
// - queue a
// - queue u
// Queue tick
// - resolve a, queues b
// - (log nothing)
// - resolve u, queues v
// - (log nothing)
// Queue tick
// - resolve b, queues c
// - log "b"
// - resolve v, queues w
// - log "v"
// Queue tick
// - resolve c, doesnt log, queues the next (ca)
// - resolve w, queues x
// - log "w"
// Queue tick
// - resolve x, queues y
// - log "x"
// - resolve ca, queues nothing
// - log "ca, c"
// Queue tick
// - resolve y, queues z
// - log "y"
// Queue tick
// - resolve z
// - log "z"
I'm not aware whether this is an actual requirement or not, so the order may change if the implementation (browser) decides to process promises directly. I think its unlikely though, as it doesn't encourage fairness and in case of a promise being chained forever and forever this promise would get all the resources assigned to it.
I think the general recommendation is to not depend on the order of promises being completed. Just think of a.then(b) as, b only happens after a is completed, not earlier, and nothing more.
If you require multiple promises to be dependent on each other, use Promise.all() or Promise.any().