Когда использовать requestIdleCallback?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда использовать requestIdleCallback?
requestIdleCallback() — это API браузера, который позволяет отложить выполнение функции до момента, когда браузер свободен и нет спешки. Это инструмент для оптимизации производительности приложения.
Что такое "idle" (холостой ход)?
Браузер никогда не стоит на месте. Он постоянно:
- Обрабатывает события пользователя (клики, скролл)
- Выполняет JavaScript
- Рисует (paint) элементы на экране
- Обновляет макет (layout)
requestIdleCallback вызывает вашу функцию только когда браузер завершит все срочные задачи и получит время для менее срочных операций. Идеально для работы, которая может подождать.
Синтаксис
const id = requestIdleCallback(callback, options);
// Отмена, если нужно
cancelIdleCallback(id);
Колбэк получает объект IdleDeadline с методом timeRemaining(), показывающим, сколько времени браузер может потратить на вашу функцию.
Практические примеры использования
1. Логирование аналитики (самый распространённый случай)
// В React компоненте
function MyComponent() {
useEffect(() => {
// Критичное: отправляем данные о рендере
console.log('Компонент отрендерен');
// Некритичное: отправляем аналитику
// Делаем это когда браузер не занят
const id = requestIdleCallback(() => {
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify({
eventType: 'component_rendered',
timestamp: Date.now()
})
});
});
// Если компонент размонтируется до idle — отменяем
return () => cancelIdleCallback(id);
}, []);
return <div>Component</div>;
}
2. Индексирование содержимого для поиска
class SearchIndex {
constructor(documents) {
this.documents = documents;
this.index = {};
this.isIndexing = false;
}
// Индексируем документы в idle-время
buildIndex() {
if (this.isIndexing) return;
this.isIndexing = true;
let docIndex = 0;
const indexNextBatch = (deadline) => {
// Пока есть время и документы
while (docIndex < this.documents.length &&
deadline.timeRemaining() > 1) {
const doc = this.documents[docIndex++];
this.indexDocument(doc);
}
// Если ещё остались документы, продолжим позже
if (docIndex < this.documents.length) {
requestIdleCallback(indexNextBatch);
} else {
this.isIndexing = false;
console.log('Индекс построен');
}
};
requestIdleCallback(indexNextBatch);
}
indexDocument(doc) {
// Разбираем текст и добавляем в индекс
const words = doc.text.toLowerCase().split(/\s+/);
words.forEach(word => {
if (!this.index[word]) this.index[word] = [];
this.index[word].push(doc.id);
});
}
}
3. Загрузка критичного контента с приоритетом
const resourceLoader = {
criticalResources: [],
nonCriticalResources: [],
load() {
// Загружаем критичное сразу
this.criticalResources.forEach(url => {
fetch(url);
});
// Некритичное загружаем в idle-время
requestIdleCallback(() => {
this.nonCriticalResources.forEach(url => {
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
document.head.appendChild(link);
});
});
}
};
4. Фоновая синхронизация
// Синхронизируем локальные данные с сервером
function syncLocalDataWhenIdle() {
requestIdleCallback(() => {
const unsyncedData = localStorage.getItem('unsyncedData');
if (unsyncedData) {
fetch('/api/sync', {
method: 'POST',
body: unsyncedData
})
.then(() => {
localStorage.removeItem('unsyncedData');
console.log('Синхронизация завершена');
})
.catch(error => {
console.error('Ошибка синхронизации:', error);
});
}
});
}
5. Отложенная инициализация плагинов
// Инициализируем третьесторонние скрипты только когда браузер свободен
function initializePlugins() {
// Критичные плагины сразу
initPaymentGateway();
// Некритичные через requestIdleCallback
requestIdleCallback(() => {
initAnalyticsPlugin();
initChatWidget();
initFeedbackWidget();
});
}
6. Обработка больших массивов данных
function processLargeDataset(data) {
let index = 0;
const processChunk = (deadline) => {
// Обрабатываем элементы пока есть время
while (index < data.length && deadline.timeRemaining() > 5) {
const item = data[index++];
processItem(item);
}
// Если ещё остались — планируем следующий чанк
if (index < data.length) {
requestIdleCallback(processChunk);
} else {
console.log('Обработка завершена');
}
};
requestIdleCallback(processChunk);
}
Сравнение: requestIdleCallback vs другие подходы
// setTimeout с минимальной задержкой
setTimeout(() => {
// Выполнится после ~4-5мс
// Может прерваться, если браузер занят
}, 0);
// requestIdleCallback
requestIdleCallback(() => {
// Выполнится когда браузер ДЕЙСТВИТЕЛЬНО свободен
// Использует оставшееся время в рамках фрейма
});
// requestAnimationFrame
requestAnimationFrame(() => {
// Выполнится перед следующей перерисовкой
// Идеально для анимаций, не для фоновых задач
});
Когда ИСПОЛЬЗОВАТЬ requestIdleCallback
Идеально для:
- Аналитики и телеметрии
- Предзагрузка ресурсов
- Индексирование контента
- Фоновая обработка данных
- Инициализация некритичных компонентов
- Синхронизация локальных данных
- Логирование и отладка
Когда НЕ использовать requestIdleCallback
// ❌ НЕПРАВИЛЬНО: для критичных операций
requestIdleCallback(() => {
// Сохранение данных формы перед отправкой
saveFormData();
});
// ✅ ПРАВИЛЬНО: сохраняем СРАЗУ
saveFormData();
// ❌ НЕПРАВИЛЬНО: для UI обновлений
requestIdleCallback(() => {
setUIState(newState);
});
// ✅ ПРАВИЛЬНО: используем setState или updateState сразу
setUIState(newState);
Параметры requestIdleCallback
// timeout гарантирует, что функция выполнится через N миллисекунд
// даже если браузер занят
requestIdleCallback(() => {
console.log('Выполнится максимум через 2 секунды');
}, { timeout: 2000 });
// Полезно для операций, которые не должны откладываться бесконечно
requestIdleCallback(() => {
uploadAnalytics();
}, { timeout: 10000 }); // Максимум 10 секунд
Поддержка браузерами
requestIdleCallback поддерживается в большинстве современных браузеров, но не в Safari (старых версий). Рекомендуется полифилл:
const scheduleCallback = (
typeof requestIdleCallback !== 'undefined'
? requestIdleCallback
: (cb) => setTimeout(cb, 1)
);
scheduleCallback(() => {
// Работает везде
});
Главное правило
requestIdleCallback — это только для работы, которую браузер может отложить без последствий. Для всего остального используй синхронный код или setTimeout.