In node the documentation here, it is said that the output of setTimeout and setImmidiate being called together is non-deterministic.
i understand that,
but what follows it is an example of using an IO wrapper callback
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
which makes the order always be:
sesetImmidiate
setTimeout
with the following explanation: The main advantage to using setImmediate() over setTimeout() is setImmediate() will always be executed before any timers if scheduled within an I/O cycle, independently of how many timers are present.
why exactly 'setImmediate() will always be executed before any timers if scheduled within an I/O cycle'?
This is because of libuv
design, in this article you can find a complete explanation how this works, here's a summary:
Libuv order execution:
while (r != 0 && loop->stop_flag == 0) {
// first timers
uv__update_time(loop);
uv__run_timers(loop);
ran_pending = uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) {
timeout = uv_backend_timeout(loop);
}
uv__io_poll(loop, timeout);
uv__run_check(loop); // check handlers - "setImmediate"
uv__run_closing_handles(loop);
if (mode == UV_RUN_ONCE) {
// second timers
uv__update_time(loop);
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
- uv__loop_alive — Check whether there are any referenced handlers to be invoked, or any active operations pending
- uv__update_time — This will send a system call to get the current time and update the loop time (This is used to identify expired timers).
- uv__run_timers — Run all expired timers
- uv__run_pending — Run all completed/errored I/O callbacks
- uv__io_poll — Poll for I/O
- uv__run_check — Run all check handlers (setImmediate callbacks will run here)
- uv__run_closing_handles — Run all close handlers
Both setTimeout
and setImmediate
are macrotasks, here's why are executed in that order, nice discussion about this here:
If scripts have been scheduled by setImmediate(), "polling" phase will set a time-out which is zero.It means that after the queue has been exhausted, "polling" phase will not wait for callbacks to be added to the queue but continue to the check phase.
If scripts have been scheduled by setTimeout(), "polling" will set a time-out which is the result of the soonest threshold of timers minus current time.Then when time out, the loop continues and finally wraps back to the timers phase.