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

В чем разница между способами объявления переменной?

1.0 Junior🔥 281 комментариев
#JavaScript Core

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

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

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

Расскажи про Event Loop в JavaScript

Event Loop — это механизм, который делает JavaScript асинхронным несмотря на то, что язык однопоточный. Это один из самых сложных и важных концептов в JavaScript.

Однопоточность JavaScript

Проблема

JavaScript выполняет код в одном потоке (main thread). Это значит, что в одный момент времени выполняется только одна операция.

// Все это выполняется в одном потоке
console.log('1');
console.log('2');
console.log('3');
// Вывод: 1, 2, 3 (по порядку)

Но если операция заблокирует поток, все остановится:

// Плохо: блокирует поток
function blockThread() {
  const start = Date.now();
  while (Date.now() - start < 5000) {
    // 5 секунд ничего не делает
  }
}

console.log('Старт');
blockThread(); // Поток заблокирован на 5 секунд!
console.log('Конец'); // Выведется только через 5 секунд

Как работает Event Loop

Три основных компонента

  1. Call Stack — стек вызовов функций
  2. Web APIs — API браузера (setTimeout, fetch, DOM)
  3. Callback Queue (Macrotask/Microtask Queue) — очередь обратных вызовов

Процесс

┌─────────────────────┐
│   Call Stack        │  <- Текущий код
└─────────────────────┘
          ↓ (когда пусто)
┌─────────────────────┐
│  Microtask Queue    │  <- Promises, queueMicrotask
└─────────────────────┘
          ↓ (когда пусто)
┌─────────────────────┐
│  Macrotask Queue    │  <- setTimeout, setInterval, I/O
└─────────────────────┘

Алгоритм Event Loop

1. Выполнить весь код из Call Stack
2. Когда Call Stack пуст:
   a. Выполнить все задачи из Microtask Queue
   b. Перерисовать (repaint) страницу
   c. Выполнить одну задачу из Macrotask Queue
3. Вернуться на шаг 2

Пример: разные типы очередей

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

setTimeout(() => {
  console.log('setTimeout'); // Macrotask Queue
}, 0);

Promise.resolve()
  .then(() => {
    console.log('Promise 1'); // Microtask Queue
  })
  .then(() => {
    console.log('Promise 2'); // Microtask Queue
  });

console.log('Конец'); // Call Stack

// Вывод:
// Начало
// Конец
// Promise 1
// Promise 2
// setTimeout

Объяснение порядка

  1. 'Начало' и 'Конец' — синхронный код из Call Stack
  2. Promise 1 и 2 — из Microtask Queue (выполняются раньше setTimeout)
  3. setTimeout — из Macrotask Queue (выполняется последним)

Call Stack

Как работает стек

function greet(name) {
  const greeting = `Hello, ${name}`;
  return greeting;
}

function sayHello() {
  const message = greet('Alice');
  console.log(message);
}

sayHello();

// Call Stack во время выполнения:
// sayHello()
//   greet('Alice')
//     return greeting
//   console.log(message)
// (пусто)

Microtask Queue

Что туда попадает

// 1. Promises
Promise.resolve().then(() => console.log('Promise'));

// 2. queueMicrotask
queueMicrotask(() => console.log('Microtask'));

// 3. MutationObserver
const observer = new MutationObserver(() => console.log('DOM changed'));
observer.observe(document.body, { childList: true });

// 4. async/await (это синтаксический сахар для Promises)
async function test() {
  await fetch('/api'); // Microtask
}

Важно: Microtask выполняются раньше repaint

const box = document.getElementById('box');

// Изменяем стиль
box.style.backgroundColor = 'red';

// Проверяем вычисленный стиль
Promise.resolve().then(() => {
  console.log(getComputedStyle(box).backgroundColor);
  // 'rgb(255, 0, 0)' или 'red' — уже красный!
});

// setTimeout исполняется после repaint
setTimeout(() => {
  console.log('После repaint');
}, 0);

// Вывод:
// red (microtask видит изменение)
// После repaint (macrotask выполнится позже)

Macrotask Queue

Что туда попадает

// 1. setTimeout / setInterval
setTimeout(() => console.log('Macrotask 1'), 0);
setInterval(() => console.log('Macrotask 2'), 1000);

// 2. setImmediate (Node.js, не во всех браузерах)
setImmediate(() => console.log('Immediate'));

// 3. I/O операции
fetch('/api').then(data => console.log('I/O'));

// 4. UI события
element.addEventListener('click', () => console.log('Click'));

// 5. requestAnimationFrame (выполняется перед repaint, но после microtasks)
requestAnimationFrame(() => console.log('Animation'));

Сложный пример

console.log('1: Start');

setTimeout(() => {
  console.log('2: setTimeout callback');
  Promise.resolve().then(() => console.log('3: Promise in setTimeout'));
}, 0);

Promise.resolve()
  .then(() => {
    console.log('4: Promise 1');
    setTimeout(() => console.log('5: setTimeout in Promise'), 0);
  })
  .then(() => {
    console.log('6: Promise 2');
  });

console.log('7: End');

// Вывод:
// 1: Start
// 7: End
// 4: Promise 1
// 6: Promise 2
// 2: setTimeout callback
// 3: Promise in setTimeout
// 5: setTimeout in Promise

Объяснение

1. Синхронный код: '1' и '7'
2. Microtask Queue: '4', '6' (обе части Promise chain)
3. Первая macrotask: '2' + её microtasks: '3'
4. Вторая macrotask: '5'

requestAnimationFrame

Особенность: выполняется ПЕРЕД repaint

console.log('Start');

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

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

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

console.log('End');

// Вывод:
// Start
// End
// Promise (microtask)
// RAF (перед repaint)
// setTimeout (macrotask)

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

Проблема: Infinite loop в Event Loop

// ПЛОХО: бесконечно добавляет обработчик
Promise.resolve()
  .then(() => {
    console.log('Привет');
    Promise.resolve().then(...); // Еще микротаск!
  });
// Это создаст бесконечный цикл микротасков!

Хорошо: есть условие выхода

let count = 0;

function processQueue() {
  if (count++ > 10) return;
  
  Promise.resolve().then(() => {
    console.log('Обработка ' + count);
    processQueue();
  });
}

processQueue();

Заключение

Event Loop — это сердце асинхронности в JavaScript. Понимание порядка выполнения (синхронный код → microtasks → macrotasks) критично для:

  • Избежания race conditions
  • Оптимизации производительности
  • Понимания async/await
  • Отладки timing-зависимых проблем

Запомни: Microtask всегда быстрее macrotask, даже если setTimeout с 0 миллисекунд.

В чем разница между способами объявления переменной? | PrepBro