PassFront
  1. PassFront
  2. База знаний
  3. Производительность в JS

Как выполнить тяжёлую задачу в JavaScript, не блокируя интерфейс?

Производительность в JS

JavaScript в браузере выполняется в одном потоке, поэтому долгий цикл блокирует отрисовку и обработку событий: страница «замирает». Есть три способа не блокировать интерфейс: разбить работу на части, отдать её другому потоку или отложить до простоя.

Разбиение на части

Работу делят на порции и между ними возвращают управление браузеру:

async function processAll(items) {
  const chunk = 200;
  for (let i = 0; i < items.length; i += chunk) {
    items.slice(i, i + chunk).forEach(handle);
    await new Promise(resolve => setTimeout(resolve, 0)); // даём отрисоваться
  }
}

Вместо setTimeout можно использовать scheduler.yield() (где доступен) или requestAnimationFrame.

Web Worker

Вычисления без доступа к DOM переносят в отдельный поток:

const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = e => render(e.data);

Основной поток остаётся свободным, но данные копируются (structured clone) — для больших массивов используют Transferable.

requestIdleCallback

Низкоприоритетную работу запускают, когда браузер простаивает:

requestIdleCallback(deadline => {
  while (deadline.timeRemaining() > 0 && queue.length) run(queue.shift());
});

Коротко для собеседования

Долгая синхронная задача блокирует единственный поток. Её режут на порции с паузами для отрисовки, выносят в Web Worker или выполняют в requestIdleCallback.

Другие вопросы раздела