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

Какие есть инструменты для time-slicing?

1.3 Junior🔥 161 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Инструменты для Time-Slicing (Распределения времени выполнения)

Time-slicing (или "нарезка времени") — это концепция, позволяющая разбивать длительные синхронные задачи на более мелкие части и выполнять их в "свободные" моменты между кадрами анимации (frame) или между обработкой пользовательского ввода, чтобы не блокировать основной поток и поддерживать высокую отзывчивость интерфейса (60 FPS). Это особенно критично для Frontend-разработки, где долгие вычисления могут привести к "фризам" (jank).

Нативные API браузера

1. requestIdleCallback

Это нативный API, который позволяет планировать выполнение фоновых задач в периоды простоя основного потока.

function processInIdleTime(deadline) {
  // deadline.timeRemaining() показывает, сколько времени доступно в текущем idle периоде
  while (deadline.timeRemaining() > \\
0 && tasks.length > 0) {
    const task = tasks.pop();
    performTask(task);
  }
  if (tasks.length > 0) {
    requestIdleCallback(processInIdleTime);
  }
}

requestIdleCallback(processInIdleTime);

Преимущества:

  • Нативная интеграция с планировщиком браузера.
  • Приоритизация: можно указать timeout для гарантированного выполнения.

Недостатки:

  • Недоступен в Safari (требуется полифил).
  • Периоды "простоя" могут быть очень короткими, особенно на слабых устройствах.

2. requestAnimationFrame + ручное разбиение

Комбинация rAF и ручного управления выполнением порций задачи.

function timeSlicedProcess(tasks, chunkSize = 10) {
  let index = 0;

  function processChunk() {
    const startTime = performance.now();
    // Выполняем порцию задач, но не дольше ~3-4мс (чтобы осталось время на рендер)
    while (index < tasks.length && performance.now() - startTime < 3) {
      processTask(tasks[index]);
      index++;
    }

    if (index < tasks.length) {
      // Планируем следующую порцию на следующий кадр анимации
      requestAnimationFrame(processChunk);
    } else {
      console.log('Все задачи завершены!');
    }
  }

  requestAnimationFrame(processChunk);
}

Преимущества:

  • Полный контроль над размером "порции" (chunk).
  • Гарантированная отзывчивость UI, так как задача уступает время рендеру каждый кадр.

Недостатки:

  • Требует ручной реализации и настройки размера порции.

Библиотеки и фреймворки

3. Scheduler API в React

React 16+ внедрил внутренний Scheduler для координации задач с разным приоритетом (например, пользовательский ввод — высокий, отложенные вычисления — низкий).

// Непрямое использование через React APIs
import { unstable_next } from 'scheduler';

// Использование с хуками (например, для тяжелых вычислений)
const deferredValue = useDeferredValue(heavyValue);

React автоматически разбивает рендеринг компонентов на чанки при включенном Concurrent Mode и использовании useTransition / useDeferredValue. Это и есть встроенный time-slicing на уровне виртуального DOM.

4. Web Workers

Хотя Workers выполняются в отдельном потоке и не являются time-slicing в чистом виде, они — главный инструмент для вынесения блокирующих задач из основного потока. Часто используется в комбинации с postMessage и разбиением задачи на части.

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ tasks: hugeArray, chunkSize: 100 });

worker.onmessage = (e) => {
  if (e.data.type === 'chunkResult') {
    updateUI(e.data.result);
  } else if (e.data.type === 'done') {
    console.log('Worker завершил работу.');
  }
};

// worker.js
self.onmessage = (e) => {
  const { tasks, chunkSize } = e.data;
  for (let i = 0; i < tasks.length; i += chunkSize) {
    const chunk = tasks.slice(i, i + chunkSize);
    const result = processChunk(chunk); // Тяжелое вычисление
    self.postMessage({ type: 'chunkResult', result });
  }
  self.postMessage({ type: 'done' });
};

5. Библиотека scheduler (от команды React)

Отдельный пакет, который можно использовать вне React.

import { unstable_scheduleCallback } from 'scheduler';

const taskId = unstable_scheduleCallback(
  unstable_IdlePriority, // Приоритет
  () => {
    // Выполнить нефризовую задачу
    performNonCriticalWork();
  }
);

Стратегии и паттерны

  • Приоритизация задач: Разделение на критические (ввод, анимация) и некритические (логирование, агрегация данных).
  • Инкрементальная обработка: Обработка данных по частям с видимым прогрессом (прогресс-бар).
  • Virtualized Lists: Библиотеки вроде react-window или vue-virtual-scroller — это time-slicing на уровне рендеринга DOM, создавая только видимые элементы.

Выбор инструмента

Выбор зависит от контекста:

  • Для фоновых задач без жестких сроковrequestIdleCallback (с полифилом).
  • Для контроля над каждым кадром анимацииrequestAnimationFrame с ручным разбиением.
  • В экосистеме React — полагайтесь на встроенный планировщик и Concurrent Features (useDeferredValue, Suspense).
  • Для тяжелых вычислений (сортировка, парсинг)Web Workers это наилучший выбор, чтобы полностью избежать блокировки основного потока.
  • Для работы с большими списками или таблицами — виртуализация.

Ключевой принцип: Основной поток должен тратить на выполнение JavaScript не более 3-4 миллисекунд за один цикл событий (event loop), чтобы оставалось ~16мс для стилей, layout, paint и composite, что обеспечивает плавные 60 FPS. Инструменты time-slicing помогают уложиться в этот бюджет.