So, in your f1()
example, you have a race between setImmediate()
and the .then()
handler of an immediately resolved promise since both will be in the event queue at the time the next event is ready to be processed.
When both are ready to run, one runs before the other because of the internals of how different async things like setImmediate()
and promises are coded to work in the node.js implementation of its event loop. Internal to the event loop in node.js, there is a sequence or priority for some different types of asynchronous operations and some go before others if all are waiting to go. It is possible, though difficult, to fully understand which goes before the others, but it is very complicated and it is mostly an implementation detail, not something fully documented by specification.
In this specific case, native promises in node.js use a microTasks queue (there are apparently a couple separate microTasks queues) and they are run before things like setImmediate()
, timers and I/O events.
But, in general, it is best to not rely on fully understanding all that and, if you want one thing to happen before the other, don't allow it to be a race between the two inside of node.js. Just code it with your own code to force the sequence you want. This also makes your code more obvious and declarative what order you expect things to be processed in.
If I read your current code, I would think that you purposely set up a race between f1()
and setImmediate()
and did not care which one ran first because the code is not declarative and does not define a desired order.
For more info on the details of the internals of different types of async operations in the event loop, you can read these references:
Promise.resolve().then vs setImmediate vs nextTick
Promises, Next-Ticks and Immediates— NodeJS Event Loop Part 3
Promises wiggle their way between nextTick
and setImmediate
Here's a quote from this last reference article:
Native promise handlers are executed on a microtask queue which is roughly the same as nextTick, so they run before everything else. Pure javascript [promise] implementations should use nextTick for scheduling.
For your f2()
example, it's probably just that fs.readFile()
takes some finite amount of time so f2()
does not resolve immediately and thus isn't ready to run at the same time that setImmediate()
is so the setImmediate()
runs before f2()
resolves.