Whether or not promise.resolve()
will synchronously or asynchronously execute its continuations really depends on the implementation.
Furthermore, the "Event Loop" is not the only mechanism to provide a different "execution context". There may be other means, for example threads or thread pools, or think of GCD (Grand Central Dispatch, dispatch lib), which provides dispatch queues.
The Promises/A+ Spec clearly requires that the continuation (the onFulfilled
respectively the onRejected
handler) will be asynchronously executed with respect to the "execution context" where the then
method is invoked.
onFulfilled
or onRejected
must not be called until the execution context stack contains only platform code. [3.1].
Under the Notes you can read what that actually means:
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.
Here, each event will get executed on a different "execution context", even though this is the same event loop, and the same "thread".
Since the Promises/A+ specification is written for the Javascript environment, a more general specification would simply require that the continuation will be asynchronously executed with respect to the caller invoking the then
method.
There are good reasons to this in that way!
Example (pseudo code):
promise = async_task();
printf("a");
promise.then((int result){
printf("b");
});
printf("c");
Assuming, the handler (continuation) will execute on the same thread as the call-site, the order of execution should be that the console shows this:
acb
Especially, when a promise is already resolved, some implementations tend to invoke the continuation "immediately" (that is synchronously) on the same execution context. This would clearly violate the rule stated above.
The reason for the rule to invoke the continuation always asynchronously is that a call-site needs to have a guarantee about the relative order of execution of handlers and code following the then
including the continuation statement in any scenario. That is, no matter whether a promise is already resolved or not, the order of execution of the statements must be the same. Otherwise, more complex asynchronous systems may not work reliable.
Another bad design choice for implementations in other languages which have multiple simultaneous execution contexts - say a multi-threaded environment (irrelevant in JavaScript, since there is only one thread of execution), is that the continuation will be invoked synchronously with respect to the resolve
function. This is even problematic when the asynchronous task will finish in a later event loop cycle and thus the continuation will be indeed executed asynchronously with respect to the call-site.
However, when the resolve
function will be invoked by the asynchronous task when it is finished, this task may execute on a private execution context (say the "worker thread"). This "worker thread" usually will be a dedicated and possibly special configured execution context - which then calls resolve
. If that resolve
function will synchronously execute the continuation, the continuation will run on the private execution context of the task - which is generally not desired.