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

В какой момент выполнения очереди происходит перерисовка страницы

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

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

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

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

Перерисовка страницы и очередь событий

Перерисовка страницы (перестановка и отрисовка элементов) происходит после полного выполнения текущего стека вызовов и перед выполнением следующей задачи из очереди задач (macrotasks).

Фазы выполнения JavaScript

Для понимания момента перерисовки важно знать, как браузер обрабатывает код:

// Примерный порядок обработки браузером:
// 1. Выполнить весь синхронный код (стек вызовов)
// 2. Выполнить все микротаски (microtasks) - Promise, MutationObserver
// 3. ПЕРЕРИСОВАТЬ страницу (repaint + reflow)
// 4. Выполнить следующую макротаску (macrotask) из очереди

Очередь задач (Event Loop)

Macrotasks (очередь задач):

  • setTimeout
  • setInterval
  • setImmediate (Node.js)
  • Обработка событий (click, input и т.д.)
  • Перестановка (layout, paint)

Microtasks (микротаски):

  • Promise.then/catch/finally
  • async/await (это синтаксический сахар над Promise)
  • MutationObserver
  • queueMicrotask()

Жизненный цикл одной итерации

console.log('1. Синхронный код');

setTimeout(() => {
  console.log('2. Макротаска (setTimeout)');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('3. Микротаска (Promise)');
  });

console.log('4. Синхронный код 2');

// Порядок вывода:
// 1. Синхронный код
// 4. Синхронный код 2
// 3. Микротаска (Promise)
// [ПЕРЕРИСОВКА СТРАНИЦЫ]
// 2. Макротаска (setTimeout)

Когда происходит перерисовка

Перерисовка (render) не происходит после каждого изменения DOM, а выполняется:

  1. После выполнения всех микротасок (Promise, async/await)
  2. Перед выполнением следующей макротаски (setTimeout, событие)
  3. Примерно каждые 16.67ms (для 60 FPS монитора)

Практический пример

// Сценарий 1: Перерисовка НЕ произойдет
function updateDOM() {
  const el = document.getElementById('box');
  el.textContent = 'Привет 1';
  el.textContent = 'Привет 2';
  el.textContent = 'Привет 3';
  // Браузер перемалует только последнее значение
}

// Сценарий 2: Перерисовка произойдет
function updateDOMWithRepaint() {
  const el = document.getElementById('box');
  el.textContent = 'Привет 1';
  
  setTimeout(() => {
    el.textContent = 'Привет 2';
  }, 0);
}

// Сценарий 3: requestAnimationFrame
function updateDOMSmooth() {
  const el = document.getElementById('box');
  el.textContent = 'Начало';
  
  requestAnimationFrame(() => {
    el.textContent = 'Анимация';
  });
}

requestAnimationFrame vs setTimeout

// setTimeout выполняется ПОСЛЕ перерисовки
setTimeout(() => {
  console.log('После перерисовки');
}, 0);

// requestAnimationFrame выполняется ПЕРЕД перерисовкой
requestAnimationFrame(() => {
  console.log('Перед перерисовкой');
});

// Порядок для 60 FPS:
// [Выполнить JavaScript]
// [Выполнить requestAnimationFrame]
// [ПЕРЕРИСОВКА СТРАНИЦЫ]
// [Ждем 16ms]
// [Выполнить setTimeout]

Оптимизация производительности

// Неправильно: много изменений в одном цикле
for (let i = 0; i < 1000; i++) {
  document.body.style.left = i + 'px';
}

// Правильно: браузер оптимизирует
const el = document.body;
for (let i = 0; i < 1000; i++) {
  el.style.left = i + 'px';
}

// Лучше всего: используем requestAnimationFrame
function animate(timestamp) {
  el.style.transform = 'translateX(' + timestamp + 'px)';
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

Заключение

Перерисовка происходит между выполнением микротасок и макротасок. Это критично для оптимальных анимаций. Используйте requestAnimationFrame для синхронизации с браузером и избегайте частых изменений DOM в цикле.