← Назад к вопросам

Когда использовать requestIdleCallback?

2.0 Middle🔥 111 комментариев
#Браузер и сетевые технологии#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда использовать 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.

Когда использовать requestIdleCallback? | PrepBro