Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
2.6 KiB
Markdown
106 lines
2.6 KiB
Markdown
---
|
|
title: Defer Non-Critical Work with requestIdleCallback
|
|
impact: MEDIUM
|
|
impactDescription: keeps UI responsive during background tasks
|
|
tags: javascript, performance, idle, scheduling, analytics
|
|
---
|
|
|
|
## Defer Non-Critical Work with requestIdleCallback
|
|
|
|
**Impact: MEDIUM (keeps UI responsive during background tasks)**
|
|
|
|
Use `requestIdleCallback()` to schedule non-critical work during browser idle periods. This keeps the main thread free for user interactions and animations, reducing jank and improving perceived performance.
|
|
|
|
**Incorrect (blocks main thread during user interaction):**
|
|
|
|
```typescript
|
|
function handleSearch(query: string) {
|
|
const results = searchItems(query)
|
|
setResults(results)
|
|
|
|
// These block the main thread immediately
|
|
analytics.track('search', { query })
|
|
saveToRecentSearches(query)
|
|
prefetchTopResults(results.slice(0, 3))
|
|
}
|
|
```
|
|
|
|
**Correct (defers non-critical work to idle time):**
|
|
|
|
```typescript
|
|
function handleSearch(query: string) {
|
|
const results = searchItems(query)
|
|
setResults(results)
|
|
|
|
// Defer non-critical work to idle periods
|
|
requestIdleCallback(() => {
|
|
analytics.track('search', { query })
|
|
})
|
|
|
|
requestIdleCallback(() => {
|
|
saveToRecentSearches(query)
|
|
})
|
|
|
|
requestIdleCallback(() => {
|
|
prefetchTopResults(results.slice(0, 3))
|
|
})
|
|
}
|
|
```
|
|
|
|
**With timeout for required work:**
|
|
|
|
```typescript
|
|
// Ensure analytics fires within 2 seconds even if browser stays busy
|
|
requestIdleCallback(
|
|
() => analytics.track('page_view', { path: location.pathname }),
|
|
{ timeout: 2000 }
|
|
)
|
|
```
|
|
|
|
**Chunking large tasks:**
|
|
|
|
```typescript
|
|
function processLargeDataset(items: Item[]) {
|
|
let index = 0
|
|
|
|
function processChunk(deadline: IdleDeadline) {
|
|
// Process items while we have idle time (aim for <50ms chunks)
|
|
while (index < items.length && deadline.timeRemaining() > 0) {
|
|
processItem(items[index])
|
|
index++
|
|
}
|
|
|
|
// Schedule next chunk if more items remain
|
|
if (index < items.length) {
|
|
requestIdleCallback(processChunk)
|
|
}
|
|
}
|
|
|
|
requestIdleCallback(processChunk)
|
|
}
|
|
```
|
|
|
|
**With fallback for unsupported browsers:**
|
|
|
|
```typescript
|
|
const scheduleIdleWork = window.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
|
|
|
|
scheduleIdleWork(() => {
|
|
// Non-critical work
|
|
})
|
|
```
|
|
|
|
**When to use:**
|
|
|
|
- Analytics and telemetry
|
|
- Saving state to localStorage/IndexedDB
|
|
- Prefetching resources for likely next actions
|
|
- Processing non-urgent data transformations
|
|
- Lazy initialization of non-critical features
|
|
|
|
**When NOT to use:**
|
|
|
|
- User-initiated actions that need immediate feedback
|
|
- Rendering updates the user is waiting for
|
|
- Time-sensitive operations
|