Rendering Opportunities

A browser can update the rendered page at rendering opportunities interleaved with JavaScript execution. Understanding when those opportunities occur helps explain why long tasks, microtasks, and animation callbacks can affect visual responsiveness.

The browser event loop processes JavaScript work and, when appropriate, reaches a rendering opportunity where it can update the visual state of the page. A rendering opportunity is not a guarantee that a new frame will be produced on every iteration of the event loop.

JavaScript execution is organized into tasks, such as timer callbacks, user-input processing, and other queued work. After a task finishes, the browser may perform microtask processing and can then perform rendering-related work if a rendering opportunity is available.

Rendering work can include running `requestAnimationFrame` callbacks, updating styles and layout when necessary, painting, and presenting the result. The exact internal pipeline and scheduling behavior are browser-dependent, so frontend code should rely on the specified scheduling APIs rather than assume a fixed sequence of internal rendering steps.

A long-running task can prevent the browser from reaching a rendering opportunity promptly. Microtasks are also important because the browser generally drains the microtask queue before proceeding to other event-loop work; repeatedly creating microtasks can therefore delay rendering even when no individual microtask is expensive.

`requestAnimationFrame` is designed for work that should occur before a browser-rendered frame. It is preferable to using a timer for visual updates because the browser can coordinate the callback with its rendering schedule.

Key points

Interview questions

What is a rendering opportunity in the browser event loop?

A rendering opportunity is a point at which the browser can perform rendering-related work and potentially update what the user sees. It occurs alongside, rather than being identical to, JavaScript task processing. The browser does not have to render a new frame at every opportunity. This is why JavaScript scheduling and rendering should be treated as related but distinct parts of browser execution.

How can a long JavaScript task affect rendering?

A long task occupies the thread that the browser needs for JavaScript and other main-thread work. While that task is running, the browser cannot promptly proceed to the next stages of event-loop processing and rendering. As a result, visual updates and input handling can be delayed. Breaking expensive work into smaller pieces can give the browser more opportunities to remain responsive.

Why can microtasks cause rendering delays?

Microtasks are processed at microtask checkpoints, and the browser normally drains the microtask queue before moving on to other event-loop work. If microtasks continually schedule additional microtasks, the queue can remain nonempty for a long time. That can postpone rendering even though each individual callback is small. Therefore, replacing one large task with an effectively unbounded chain of microtasks does not necessarily improve responsiveness.

Why should animation code generally use `requestAnimationFrame` rather than `setTimeout`?

`requestAnimationFrame` is specifically intended for callbacks that update visual state before a browser-rendered frame. It lets the browser coordinate the callback with its rendering schedule and avoids asking for unnecessary visual work when the page is not being presented in the same way. A timer instead creates timer-based task scheduling that is not inherently synchronized with rendering. This makes `requestAnimationFrame` the usual API for frame-driven animation.

A page has a smooth animation until a promise-based loop starts doing work. What would you investigate?

I would first inspect whether the promise chain is creating a large or continuously replenished microtask queue. Since microtasks can be processed before the browser proceeds to rendering, a chain of promise callbacks can consume enough main-thread time to delay visual updates. I would profile task and microtask activity and determine whether the computation can periodically yield through a task boundary. I would also inspect `requestAnimationFrame` duration and other long-running main-thread work.