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

Как работает очередь задач?

2.0 Middle🔥 121 комментариев
#Soft Skills и рабочие процессы

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

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

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

Как работает очередь задач (Event Loop)

Это один из самых важных вопросов для понимания JavaScript. Очередь задач (task queue) — это ключевой механизм асинхронного выполнения кода.

1. Однопоточная природа JavaScript

JavaScript выполняет код в одном потоке, но может обрабатывать асинхронные операции благодаря event loop:

console.log('1. Start');

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

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

console.log('4. End');

// Вывод:
// 1. Start
// 4. End
// 2. Promise
// 3. setTimeout

2. Call Stack, Task Queue, Microtask Queue

Важно понимать три компонента:

// Call Stack - текущий выполняемый код
function outer() {
  console.log('outer start');
  inner();
  console.log('outer end');
}

function inner() {
  console.log('inner');
}

outer();
// Call Stack: outer() -> inner() -> back to outer() -> пусто

// Task Queue - макротаски (setTimeout, setInterval, I/O)
setTimeout(() => console.log('task'), 0);

// Microtask Queue - микротаски (Promise, async/await, MutationObserver)
Promise.resolve().then(() => console.log('microtask'));

3. Event Loop - Алгоритм

// Упрощённое представление Event Loop:

// while (eventLoop.waitForTask()) {
//   const oldestTask = eventLoop.nextTask();
//   execute(oldestTask);
//   
//   while (microtaskQueue.hasMicrotasks()) {
//     const microtask = microtaskQueue.shift();
//     execute(microtask);
//   }
//   
//   if (needsRepaint) {
//     repaint();
//   }
// }

// Это означает:
// 1. Выполнить одну задачу из Task Queue
// 2. Выполнить ВСЕ задачи из Microtask Queue
// 3. Если нужно, перерисовать (requestAnimationFrame)
// 4. Повторить

4. Примеры с разными типами операций

console.log('Start'); // Call Stack, выполняется сразу

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

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

setImmediate(() => {
  console.log('setImmediate');
}); // Task Queue (node.js только)

requestAnimationFrame(() => {
  console.log('requestAnimationFrame');
}); // Рисование, не задача

console.log('End'); // Call Stack

// Порядок выполнения:
// 1. Start (Call Stack)
// 2. End (Call Stack)
// 3. Promise 1 (Microtask)
// 4. Promise 2 (Microtask)
// 5. setTimeout 1 (Task)
// 6. setImmediate (Task, node.js)
// 7. requestAnimationFrame (перед следующим frame)

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

// МИКРОТАСКИ - выполняются в ПРИОРИТЕТЕ:
// - Promise.then/catch/finally
// - async/await
// - queueMicrotask()
// - MutationObserver
// - Process.nextTick (Node.js)

// МАКРОТАСКИ (обычные задачи):
// - setTimeout
// - setInterval
// - setImmediate (Node.js)
// - requestAnimationFrame
// - I/O операции
// - UI события (клик, скролл)

// Почему это важно:
Promise.resolve().then(() => {
  console.log('микротаска быстрая');
});

setTimeout(() => {
  console.log('макротаска медленнее');
}, 0);

// Микротаска ВСЕГДА выполнится первой, несмотря на setTimeout(0)

6. Практический пример с async/await

async function example() {
  console.log('1. Синхронный код в async функции');
  
  await Promise.resolve();
  // await переводит остаток функции в микротаску
  
  console.log('2. После await (это микротаска)');
}

example();
console.log('3. После вызова async функции');

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

// Вывод:
// 1. Синхронный код в async функции
// 3. После вызова async функции
// 2. После await (это микротаска)
// 4. setTimeout

7. Проблемы и optimization

// Проблема: Blocking Event Loop
function heavyComputation() {
  for (let i = 0; i < 1000000000; i++) {
    // Очень долгое вычисление
  }
}

heavyComputation(); // Зависает весь UI
button.click(); // Событие не обработается, пока вычисление не закончится

// Решение 1: setTimeout для разбиения на части
function heavyComputationAsync(size = 0) {
  if (size < 1000000000) {
    setTimeout(() => {
      // Небольшое вычисление
      heavyComputationAsync(size + 1000);
    }, 0);
  }
}

// Решение 2: Web Worker
const worker = new Worker('heavy-computation.js');
worker.postMessage(data);
worker.onmessage = (e) => {
  console.log('Результат:', e.data);
};

8. Практический пример: правильная обработка асинхронных операций

function fetchUserWithLogging() {
  console.log('1. Before fetch');
  
  fetch('/api/user')
    .then(response => {
      console.log('4. Got response');
      return response.json();
    })
    .then(data => {
      console.log('5. Got data:', data);
    })
    .catch(error => {
      console.error('6. Error:', error);
    });
  
  console.log('2. After fetch call');
}

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

fetchUserWithLogging();

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

// Вывод примерно такой:
// 1. Before fetch
// 2. After fetch call
// 3. Microtask
// 4. Got response (когда fetch завершится)
// 5. Got data
// 7. setTimeout

9. Современный подход: async/await

// Старый способ - Callback Hell
function getUser(id, callback) {
  setTimeout(() => callback({ id, name: 'John' }), 100);
}

getUser(1, (user) => {
  console.log('User:', user);
});

// Лучше - Promise
function getUserPromise(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, name: 'John' }), 100);
  });
}

getUserPromise(1).then(user => console.log('User:', user));

// Лучше всего - async/await
async function getUserAsync(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, name: 'John' }), 100);
  });
}

async function main() {
  const user = await getUserAsync(1);
  console.log('User:', user);
}

main();

Заключение

Очередь задач (Event Loop) - это механизм, позволяющий JavaScript работать асинхронно несмотря на однопоточность. Ключная идея: микротаски (Promises) имеют приоритет над макротасками (setTimeout). Понимание Event Loop критично для написания производительного и предсказуемого кода.