When a timer is involved, nodejs creates a timer in the timers heap and schedules the setImmediate macrotask in the queue and the event loop starts from the timers phase. At this moment, since the timer is not expired yet (due to the large expiration period), event loop moves to the immediates phase (assume there are no microtasks scheduled. e.g, next ticks/resolved promises).
For your scenario to happen, event loop should hang before checking the timers queue until the timer is expired. This can happen if the setTimeout and the setImmediate is called inside a close handler along with another set of microtasks. When the close handler is executed, the event loop is in the close handler phase. If the close handler repeatedly adds microtasks into the microtask queues (next tick/resolved promises) event loop will keep processing the microtask queues before moving to the timers phase. If event loops takes more time to process the microtasks than the timer timespan, there’s a chance for timer callback to be executed before the setImmediate.
And Yes. We cannot guarantee the execution order of the setTimeout callback and the setImmediate callback as its behavior can based on the call site and many other facts. For guaranteed execution, we must use chained events.
