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

Блокирует ли Event Loop перерендер HTML?

2.0 Middle🔥 161 комментариев
#JavaScript Core#Браузер и сетевые технологии

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

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

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

Блокирование потока в JavaScript при ожидании асинхронных операций

Это трюкий вопрос, касающийся природы JavaScript. Дам полный ответ:

Главный пункт: JavaScript однопоточен

Ничего не блокирует основной поток при ожидании асинхронных операций. JavaScript использует event loop и callback queue, а не блокирующие операции.

Как на самом деле работают асинхронные операции

1. Event Loop

JavaScript имеет архитектуру на основе event loop:

// 1. Синхронный код выполняется немедленно
console.log('Start');  // Выполнится первым

// 2. Асинхронный код регистрируется, НО не блокирует
setTimeout(() => {
  console.log('After 1000ms');  // Выполнится позже
}, 1000);

// 3. Остальной синхронный код выполняется
console.log('End');  // Выполнится вторым

// Порядок вывода:
// Start
// End
// After 1000ms

Event loop работает так:

┌─────────────────────┐
│   Call Stack        │  <- Выполняется текущий код
└─────────────────────┘
         ↓
┌─────────────────────┐
│  Task Queue         │  <- setTimeout, setInterval callbacks
│  (Macrotask Queue)  │
└─────────────────────┘
         ↓
┌─────────────────────┐
│ Microtask Queue     │  <- Promise callbacks, queueMicrotask
└─────────────────────┘
         ↓
┌─────────────────────┐
│  Render Tasks       │  <- Отрисовка страницы
└─────────────────────┘

2. Callback Queue (Очередь обратных вызовов)

Асинхронные операции помещаются в очередь, а не блокируют выполнение:

console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);  // Даже с 0 ms задержкой!

console.log('3');

// Вывод:
// 1
// 3
// 2  <- Выполнится только когда call stack пуст

Почему так? Потому что setTimeout помещает callback в Macrotask Queue, а не в Call Stack.

Микротаски vs Макротаски

Микротаски (Microtasks) — выше приоритет

Выполняются между каждой макротаской:

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

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

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

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

// Вывод:
// Microtask 1
// Microtask 2
// Macrotask 1
// Macrotask 1

Микротаски:

  • Promise.then(), Promise.catch(), Promise.finally()
  • queueMicrotask()
  • MutationObserver

Макротаски:

  • setTimeout()
  • setInterval()
  • setImmediate() (Node.js)
  • I/O операции
  • UI events

Что на самом деле "блокирует"

Ничего не блокирует основной поток для асинхронных операций. Но есть случаи синхронного блокирования:

1. Долгие синхронные операции

// Это БЛОКИРУЕТ UI!
function expensiveComputation() {
  let sum = 0;
  for (let i = 0; i < 10_000_000_000; i++) {
    sum += i;  // Очень долгая операция
  }
  return sum;
}

console.log('Start');
expensiveComputation();  // БЛОКИРУЕТ весь остальной код
console.log('End');      // Выполнится только после завершения

2. Синхронный fetch (устарело)

// В старых браузерах
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', false);  // false = синхронный
xhr.send();  // БЛОКИРУЕТ весь остальной код!

console.log(xhr.responseText);

Async/Await - не блокирует

Важно: async/await НЕ блокирует основной поток! Это синтаксический сахар над Promise:

// Это НЕ блокирует
async function fetchData() {
  const response = await fetch('/api/data');  // Не блокирует!
  return response.json();
}

console.log('Start');
fetchData().then(() => console.log('Done'));
console.log('End');  // Выполнится ДО завершения fetch!

// Вывод:
// Start
// End
// Done

Как это работает внутри:

// async/await это просто Promise с then/catch
async function example() {
  const result = await somePromise();
  return result;
}

// Эквивалентно:
function example() {
  return somePromise().then(result => result);
}

Как правильно ждать результатов

1. Promise

fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

2. Async/Await

async function loadData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

loadData();  // Не блокирует!

3. Promise.all() - ждать нескольких операций

// Не блокирует основной поток
const [users, posts, comments] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]);

Реальный пример: блокирующий vs неблокирующий код

// БЛОКИРУЮЩИЙ КОД (плохо)
function slowDownUI() {
  const start = performance.now();
  while (performance.now() - start < 5000) {  // Блокирует на 5 сек
    // Бесконечный цикл
  }
  console.log('Done');
}

// НЕБЛОКИРУЮЩИЙ КОД (хорошо)
async function dontSlowDownUI() {
  await new Promise(resolve => setTimeout(resolve, 5000));
  console.log('Done');  // UI не зависает!
}

// Или с запросом
async function fetchWithoutBlocking() {
  const response = await fetch('/api/data');  // Не блокирует
  const data = await response.json();
  return data;  // UI свободен для клика, скролла и т.д.
}

Ответ на вопрос

Что блокирует поток при ожидании отложенной операции?

Ответ: Ничего. JavaScript не блокирует поток. Вместо этого:

  1. Асинхронная операция начинается (fetch, setTimeout, Promise)
  2. Код продолжает выполняться (не ждёт результата)
  3. Когда результат готов, callback добавляется в queue
  4. Event loop подхватывает callback и выполняет его

Исключения:

  • Долгие синхронные операции БЛОКИРУЮТ (плохая практика)
  • Синхронный fetch БЛОКИРУЕТ (устарелый подход)
  • Использование async/await НЕ блокирует (это не sleep!)

Это одна из главных особенностей JavaScript — неблокирующий I/O.

Блокирует ли Event Loop перерендер HTML? | PrepBro