I am sure that all the codes below, except resolve(Promise.resolve(p2)), are synchronous.
No. then callbacks are never called synchronously, they're always asynchronous (and that's good). That means their relative order is indeterminate, and depends on when the respective promise was fulfilled. If you ever care about the order of callbacks, make it explicit by chaining the promises onto each other.
The order of fulfillment in your example depends on the value that you resolved the promises with. Notice that the new Promise callback and therefore your resolve are synchronous, in both p1 and p2. Every resolution is put into a queue - at least with native ES6 promises that you appear to be using. The difference is that your p2 is resolved with Promise.resolve("p2"), which will resolve p2 with the result of that other promise - which is put back onto the queue again. Therefore the fulfillment of p1 happens first, and the callback is called before the fulfillment callback of p2.
So what happens step-by-step is
- The
new Promise calls the constructor callback, which in turn
resolves the new promise with the value "p1" - fulfilling it
- The
new Promise returns and the value is assigned to p1.
- The
new Promise calls the constructor callback, which in turn
- constructs another promise fulfilled with the value
"p2"
- and
resolves the new promise with it - which adds another resolve as a callback for when that promise completes
- The inner promise schedules that callback as it is already is fulfilled
- The
new Promise returns and the value is assigned to p2.
- The
p2.then is called and registers the fulfillment callback
- The
p1.then is called and schedules the fulfillment callback, as p1 is already fulfilled
After that, asynchronously:
- the inner promise's callback is called and resolves
p2 with the value "p2 - fulfilling it and scheduling its registered callbacks
- the
p1 callback is called and logs "p1"
- the
p2 callback is called and logs "p2"