Because according to the specification promises, their resolution handler is called AFTER the current thread of execution is untwisted and ends with "platform code". This ensures that they are always called asynchronously.
So, you first see console.log('b') when this thread of execution ends, and then the permission handler is called where you see console.log('a') .
From Promises / A + specification :
2.2.4 onFulfilled or onRejected should not be called until execution. The context stack contains only platform code. [3.1].
And here [3.1]:
Here, "platform code" means the engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected are executed asynchronously after the event that is then called, and with a new stack. This can be implemented either using a macro task mechanism, such as setTimeout or setImmediate, or a micro task mechanism, such as MutationObserver or process.nextTick. Since the implementation of promises is considered the platform code, it itself may contain scheduling task queues or a "trampoline" in which handlers are called.
This is done to ensure a consistent execution order, so no matter when the promise is resolved (synchronously or asynchronously), then() handlers always call at the same time relative to other code. Because many promises are resolved asynchronously, the only way to make this promise consistent no matter how it is resolved is to make them always call their .then() handlers asynchronously.
source share