Revision control

Copy as Markdown

Other Tools

# Front-end Performance
## Non-blocking Large UI Renders
When large UI renders are required, [it's important not to block the UI for more
take longer than that.
To avoid locking the UI, and ensuring the user perceives a responsive interface,
to schedule prioritized tasks.
If a later call can make an in-progress chunked render stale (for example, the
same UI is re-rendered with new data before the previous render finished), use
an `AbortController` so the stale render stops appending to the DOM instead of
racing with the new one. Check `signal.throwIfAborted()` at each chunk boundary,
and let the caller `abort()` the previous controller before starting a new
render.
Example:
```js
let renderAbortController = null;
async function renderItems(items) {
// Abort any render still in progress so it doesn't keep appending after
// this newer render has started.
renderAbortController?.abort();
renderAbortController = new AbortController();
const { signal } = renderAbortController;
// This callback contains the actual work. It can be run by the scheduler when
// available, or directly in environments where scheduler is unavailable.
const renderChunked = async () => {
signal.throwIfAborted();
let count = 0;
for (const item of items) {
renderItem(item);
// After every 50 items, yield back to the main thread so the UI can
// process input, painting, and other queued work before continuing.
// This number should be adjusted based on the expected render time
// of each item.
if (++count == 50) {
count = 0;
signal.throwIfAborted();
await globalThis.scheduler?.yield();
}
}
};
try {
// Use scheduler.postTask when available so this work is scheduled with an
// explicit priority instead of running immediately on the current call stack.
if (globalThis.scheduler) {
await globalThis.scheduler.postTask(renderChunked, {
signal
});
} else {
// Fallback for environments that do not expose scheduler, such as some tests.
await renderChunked();
}
} catch (error) {
// Swallow the exception raised by our own throwIfAborted() call, but
// rethrow anything unexpected.
if (!signal.aborted) {
throw error;
}
}
}
```