ブラウザのイベントループでは、タスク(task、いわゆる macrotask)のあとにマイクロタスクを処理します。Promise.then / catch / finally のコールバックはマイクロタスク、setTimeout はタスクとしてキューに入ります。
同じ同期コードの実行中に両方を登録しても、同期部分が終わったあと、先にマイクロタスクが空になるまで処理されます。その後に setTimeout(0) が走ります。setTimeout を先に書いていても、この順序は変わりません。
意図的にマイクロタスクへ載せたい場合は queueMicrotask() もあります。
参考文献
- In depth: Microtasks and the JavaScript runtime environment - MDN — タスクとマイクロタスクの処理順
- Using microtasks in JavaScript with queueMicrotask() - MDN — マイクロタスクキューの位置づけ