Why are javascript promises asynchronous when calling only synchronous functions?
Solution 1:
The callback passed to a Promise constructor is always called synchronously, but the callbacks passed into then
are always called asynchronously (you could use setTimeout
with a delay of 0
in a userland implementation to achieve that).
Simplifying your example (and giving the anonymous function's names so I can refer to them) to:
Promise.resolve().then(function callbackA () {
console.log("finish run 1");
}).then(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve().then(function callbackC () {
console.log("finish run 2");
})
Still gives the output in the same order:
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
Events happen in this order:
- The first promise is resolved (synchronously)
- callbackA is added to the event loop's queue
- The second promise is resolved
- callbackC is added to the event loop's queue
- There is nothing left to do so the event loop is accessed, callbackA is first in the queue so it is executed, it doesn't return a promise so the intermediate promise for callbackB is immediately resolved synchronously, which appends callbackB to the event loop's queue.
- There is nothing left to do so the event loop is accessed, callbackC is first in the queue so it is executed.
- There is nothing left to do so the event loop is accessed, callbackB is first in the queue so it is executed.
The easiest way I can think of to work around your problem is to use a library that has an Promise.prototype.isFulfilled function you can use to decide whether to call your second callback synchronously or not. For example:
var Promise = require( 'bluebird' );
Promise.prototype._SEPH_syncThen = function ( callback ) {
return (
this.isPending()
? this.then( callback )
: Promise.resolve( callback( this.value() ) )
);
}
Promise.resolve()._SEPH_syncThen(function callbackA () {
console.log("finish run 1");
})._SEPH_syncThen(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve()._SEPH_syncThen(function callbackC () {
console.log("finish run 2");
})
This outputs:
finish run 1
surprisingly this happens after run 2 finishes
finish run 2