Because, by specification, promises call their resolve handler AFTER the current thread of execution unwinds and finishes back to "platform code". That guarantees that they are always called asynchronously.
So, thus you see the console.log('b')
first as that thread of execution finishes and then the resolve handler is called where you see the console.log('a')
.
From the Promises/A+ specification:
2.2.4 onFulfilled or onRejected must not be called until the execution
context stack contains only platform code. [3.1].
And, here's note [3.1]:
Here “platform code” means engine, environment, and promise
implementation code. In practice, this requirement ensures that
onFulfilled and onRejected execute asynchronously, after the event
loop turn in which then is called, and with a fresh stack. This can be
implemented with either a “macro-task” mechanism such as setTimeout or
setImmediate, or with a “micro-task” mechanism such as
MutationObserver or process.nextTick. Since the promise implementation
is considered platform code, it may itself contain a task-scheduling
queue or “trampoline” in which the handlers are called.
This is done to provide consistent execution order so no matter when the promise is resolved (synchronously or asynchronously), the then()
handlers are always called in the same timing relative to other code. Since many promises are resolved asynchronously, the only way to make a given promise consistent no matter how it is resolved is to make them always call their .then()
handlers asynchronously.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…