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

Что такое RunLoop?

2.2 Middle🔥 91 комментариев
#Браузер и сетевые технологии

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

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

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

RunLoop (Цикл выполнения)

RunLoop — это концепция, которая чаще всего применяется в iOS/macOS разработке. Однако, аналог RunLoop существует и в JavaScript — это Event Loop. В контексте Frontend Development мы говорим об Event Loop, который работает как RunLoop в браузере.

RunLoop в iOS (для контекста)

В iOS/macOS RunLoop — это бесконечный цикл, который обрабатывает события приложения:

while (true) {
  // 1. Проверь входящие события (тапы, клики, sensor события)
  // 2. Обработай синхронный код
  // 3. Отправь результаты в UI
  // 4. Жди следующее событие
}

Event Loop в JavaScript (веб-аналог)

В браузере Event Loop работает как RunLoop и выглядит так:

// Упрощенное представление Event Loop
while (eventLoop.waitForTask()) {
  // 1. Выполни макротаск (setTimeout, fetch, user events)
  if (macrotaskQueue.hasTasks()) {
    const macrotask = macrotaskQueue.pop();
    macrotask.execute();
  }

  // 2. Выполни все микротаски (Promise, queueMicrotask)
  while (microtaskQueue.hasTasks()) {
    const microtask = microtaskQueue.pop();
    microtask.execute();
  }

  // 3. Перерендери UI если нужно
  if (needsRepaint) {
    render();
  }

  // 4. Жди следующее событие
}

Этапы Event Loop (в браузере)

1. Макротаски (Macrotasks)

Макротаски выполняются одна за раз, затем проверяются микротаски:

// Примеры макротасков:
- setTimeout/setInterval
- fetch (network requests)
- User events (click, scroll, etc.)
- requestAnimationFrame
- HTML parsing
- DOM manipulation

Пример:

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

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

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

// Вывод:
// Синхронный код
// Макротаск 1
// Макротаск 2

2. Микротаски (Microtasks)

Микротаски выполняются ДО переходом к следующему макротаску:

// Примеры микротасков:
- Promise.then/catch/finally
- queueMicrotask()
- async/await
- MutationObserver

Пример:

console.log('1');

Promise.resolve()
  .then(() => console.log('2'));

setTimeout(() => {
  console.log('3');
}, 0);

console.log('4');

// Вывод:
// 1
// 4
// 2
// 3

Полная визуализация Event Loop

Call Stack          Event Loop Flow
┌───────────┐
│ Global    │         1. Выполни синхронный код (Call Stack)
├───────────┤         2. Call Stack пуст?
│ function  │            Да -> перейди к шагу 3
└───────────┘            Нет -> продолжи выполнение
                      3. Выполни все микротаски (Promise, queueMicrotask)
 Microtask Queue     4. Есть макротаски?
 ┌───────────┐         Да -> выполни ОДИН макротаск
 │ Promise   │         Нет -> жди новое событие
 │ qMicro    │      5. Вернись к шагу 2
 └───────────┘

 Macrotask Queue
 ┌───────────┐
 │ setTimeout│
 │ fetch     │
 │ events    │
 └───────────┘

Практический пример: порядок выполнения

console.log('Начало');

// Макротаск
setTimeout(() => {
  console.log('setTimeout 1');
  Promise.resolve().then(() => console.log('Promise внутри setTimeout'));
}, 0);

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

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

// Синхронный код
console.log('Конец');

/* ВЫВОД:
Начало
Конец
Promise 1
Promise 2
setTimeout 1
Promise внутри setTimeout
setTimeout 2

ОБЪЯСНЕНИЕ:
1. console.log('Начало') -> синхронно
2. setTimeout добавляется в macrotask queue
3. Promise добавляется в microtask queue
4. setTimeout добавляется в macrotask queue
5. console.log('Конец') -> синхронно
6. Call Stack пуст -> проверяем microtask queue
7. Выполняем все микротаски (оба Promise)
8. Выполняем ОДИН макротаск (setTimeout 1)
9. Проверяем microtask queue -> выполняем Promise
10. Выполняем ОДИН макротаск (setTimeout 2)
*/

Пример с fetch (асинхронный запрос)

console.log('Start');

fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log('Data:', data));

setTimeout(() => {
  console.log('Timeout');
}, 0);

console.log('End');

/* ВЫВОД (примерно):
Start
End
Data: {...}    <- Promise микротаск выполнен
Timeout        <- setTimeout макротаск выполнен

ОБЪЯСНЕНИЕ:
1. fetch запускает сетевой запрос (макротаск)
2. .then() добавляется в microtask queue
3. setTimeout добавляется в macrotask queue
4. После сетевого ответа, микротаски выполняются ПЕРЕД setTimeout
*/

requestAnimationFrame (рисование)

requestAnimationFrame (RAF) выполняется ПЕРЕД переходом к следующему макротаску:

console.log('1');

requestAnimationFrame(() => {
  console.log('RAF 1');
});

setTimeout(() => {
  console.log('Timeout');
  requestAnimationFrame(() => {
    console.log('RAF inside Timeout');
  });
}, 0);

// Вывод:
// 1
// RAF 1
// Timeout
// RAF inside Timeout

Практический пример: оптимизация производительности

// Плохо: блокирует Event Loop
function slowSync() {
  for (let i = 0; i < 1000000000; i++) {
    // Тяжелые вычисления
  }
}

slowSync(); // Страница зависает

// Хорошо: разбиваешь на части
function slowAsync() {
  return new Promise((resolve) => {
    setTimeout(() => {
      for (let i = 0; i < 100000000; i++) {
        // Часть вычислений
      }
      resolve();
    }, 0);
  });
}

await slowAsync(); // Event Loop может обработать события

// Или с requestIdleCallback
requestIdleCallback(() => {
  // Выполняется когда браузер не занят
});

Запомни очередность

1. Синхронный код (Call Stack)
   ↓
2. Микротаски (Promise, queueMicrotask)
   ↓
3. Рисование (репаинт, рефлоу) ← requestAnimationFrame
   ↓
4. ОДИН Макротаск (setTimeout, fetch, события)
   ↓
5. GOTO Шаг 1

Инструменты для отладки Event Loop

В Chrome DevTools:

  • Вкладка Performance — видишь все события и время выполнения
  • Console — может показать что выполняется в какой момент
// Логирование Event Loop
console.time('Event Loop');
console.timeEnd('Event Loop');

Заключение

Event Loop (веб-аналог iOS RunLoop) — это сердце браузера. Он управляет:

  • Выполнением синхронного кода
  • Обработкой асинхронных операций (Promise, setTimeout)
  • Рисованием UI
  • Обработкой пользовательских событий

Понимание Event Loop критично для оптимизации производительности, избежания блокировки UI и написания эффективного асинхронного кода в JavaScript.

Что такое RunLoop? | PrepBro