The key output order is still running -> UnhandledPromiseRejection: boom. Promise rejection reporting is asynchronous, so sync code can continue first.
A Node callback, timer, I/O task, stream, or worker-pool job runs later than expected. This is critical for background jobs, test failures, queue consumers, and process-level error monitoring.
Thinking Promise.reject throws like a normal synchronous throw.
Separate sync work, microtasks, timers, I/O, check callbacks, and worker-pool work in your mental model.
setImmediate(runAfterIO); // Avoid long nextTick loops that starve other phases.
The console lane proves the visible order: still running -> UnhandledPromiseRejection: boom. The active line and queues show why source order changed.
Add timestamps around callbacks and inspect Node diagnostics, logs, or profiler output to confirm the runtime phase that is delaying work.
Related cases
Continue with nearby confusion while the mental model is fresh.
process.nextTick vs Promise
This matters in Node libraries, stream internals, CLIs, and tests that mix nextTick with promises.
setImmediate after I/O
This appears in servers, CLIs, file watchers, and test suites that schedule follow-up work after I/O.
EventEmitter listeners are sync
This explains surprising stack traces and error timing in Express apps, streams, queues, and internal event buses.
Was this clear?
This stays local for now. It helps you track where the product still needs sharper cases.