Your note
Promises go to the microtask queue and setTimeout to the macrotask queue. After each macrotask the event loop runs one microtask, then renders.
- Fix
Not one: the whole microtask queue is drained, including microtasks queued while it runs. The spec calls it a microtask checkpoint.
- Deeper
Rendering is a chance, not a step. The browser may skip it between tasks, so measure with requestAnimationFrame.
- Fact
A promise chain that never ends blocks painting just like a while(true) loop.