Learn JavaScript

queueMicrotask() - постановка микрозадачи в JavaScript

queueMicrotask() нужен, чтобы поставить функцию в очередь микрозадач. Такая функция выполнится не сразу в той же строке, а после завершения текущего синхронного кода, но раньше обычных таймеров вроде setTimeout().

Это полезный инструмент, когда нужно отложить небольшой кусок логики на самый ближайший момент после текущего выполнения: синхронизировать состояние, выровнять порядок вызовов или аккуратно завершить текущий шаг работы, не уходя в более позднюю очередь макрозадач.

Синтаксис

Базовая запись выглядит так:

queueMicrotask(callback)

Где:

  • callback - функция, которую нужно поставить в очередь микрозадач

Простейший пример:

queueMicrotask(() => {
  console.log("Микрозадача выполнена");
});

Что принимает queueMicrotask()

queueMicrotask() принимает одну функцию.

queueMicrotask(() => {
  console.log("run");
});

В отличие от setTimeout(), здесь нет задержки в миллисекундах и нет дополнительных аргументов для передачи в колбэк. Если нужно передать значения, обычно используют замыкание.

const userName = "Анна";

queueMicrotask(() => {
  console.log(userName);
});

Что возвращает queueMicrotask()

queueMicrotask() ничего полезного не возвращает.

const result = queueMicrotask(() => {
  console.log("run");
});

console.log(result); // undefined

Главный эффект здесь не в возвращаемом значении, а в том, что функция попадает в очередь микрозадач.

Простой пример

console.log("start");

queueMicrotask(() => {
  console.log("microtask");
});

console.log("end");

Результат будет таким:

start
end
microtask

Это показывает главное поведение метода: колбэк не выполняется мгновенно, но и не откладывается так далеко, как обычный таймер.

queueMicrotask() выполняется после текущего синхронного кода

Это базовое правило.

console.log("A");

queueMicrotask(() => {
  console.log("B");
});

console.log("C");

Результат:

A
C
B

Сначала JavaScript доходит до конца текущего синхронного блока, и только потом начинает выполнять микрозадачи.

Микрозадача выполняется раньше setTimeout(..., 0)

Это один из самых важных практических нюансов.

console.log("start");

queueMicrotask(() => {
  console.log("microtask");
});

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

console.log("end");

Результат будет таким:

start
end
microtask
timeout

Хотя setTimeout(..., 0) тоже откладывает выполнение совсем ненадолго, микрозадача всё равно выполнится раньше.

Несколько queueMicrotask() выполняются в порядке добавления

Это тоже полезно понимать точно.

queueMicrotask(() => {
  console.log("microtask 1");
});

queueMicrotask(() => {
  console.log("microtask 2");
});

Результат:

microtask 1
microtask 2

Если вы ставите несколько микрозадач подряд, они выполняются по очереди.

Микрозадача может поставить в очередь ещё одну микрозадачу

И она тоже выполнится до перехода к таймерам.

queueMicrotask(() => {
  console.log("first");

  queueMicrotask(() => {
    console.log("second");
  });
});

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

Результат:

first
second
timeout

Это полезная возможность, но с ней важно быть аккуратным: если бесконечно добавлять новые микрозадачи из микрозадач, можно надолго задержать таймеры, рендеринг и остальную работу цикла событий.

queueMicrotask() не ставит код на паузу

Это важно понимать правильно.

queueMicrotask() ничего не ждёт и не блокирует. Он только планирует выполнение функции на ближайший момент после завершения текущего синхронного кода.

console.log("A");

queueMicrotask(() => {
  console.log("B");
});

console.log("C");

Синхронная часть продолжает выполняться сразу. Метод не делает паузу и не останавливает программу.

Чем queueMicrotask() отличается от Promise.resolve().then()

Эти подходы похожи по моменту выполнения, но это не одно и то же.

queueMicrotask(() => {
  console.log("microtask");
});

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

Оба колбэка попадают в очередь микрозадач и выполняются очень рано. Но есть отличия:

  • queueMicrotask() явно ставит функцию в очередь микрозадач
  • Promise.resolve().then() делает это через промис
  • queueMicrotask() не создаёт новый промис
  • ошибка внутри queueMicrotask() ведёт себя как обычное необработанное исключение
  • ошибка внутри then() приводит к отклонённому промису и может стать unhandledRejection

Для простой постановки микрозадачи queueMicrotask() обычно выглядит прямее и понятнее.

Чем queueMicrotask() отличается от setTimeout()

Это сравнение особенно полезно запомнить.

  • queueMicrotask() ставит колбэк в очередь микрозадач
  • setTimeout() ставит колбэк в очередь таймеров
  • микрозадача выполняется раньше таймера
  • setTimeout() умеет откладывать выполнение на миллисекунды, а queueMicrotask() нет

Пример:

queueMicrotask(() => {
  console.log("microtask");
});

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

Здесь сначала сработает microtask, а потом timeout.

Когда это полезно

queueMicrotask() особенно удобен, когда:

  • нужно выполнить код сразу после текущего синхронного шага
  • нужно выровнять порядок выполнения без таймера
  • нужно отложить небольшую служебную логику на ближайший момент
  • нужно синхронизировать одинаковое поведение между синхронными и асинхронными ветками
  • важно выполнить код раньше, чем сработают обычные таймеры

Пример:

function notify(value) {
  queueMicrotask(() => {
    console.log("Обновлённое значение:", value);
  });
}

console.log("start");
notify(42);
console.log("end");

Результат будет таким:

start
end
Обновлённое значение: 42

Частые ошибки

Думать, что queueMicrotask() выполняет код сразу

console.log("start");

queueMicrotask(() => {
  console.log("run");
});

console.log("end");

Сначала завершится текущий синхронный код, и только потом выполнится микрозадача.

Путать queueMicrotask() и setTimeout(..., 0)

queueMicrotask(() => {
  console.log("microtask");
});

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

Это не одно и то же. Микрозадача выполнится раньше таймера.

Вызывать функцию сразу вместо передачи функции

queueMicrotask(console.log("Привет"));

Здесь console.log("Привет") выполнится немедленно, а в queueMicrotask() попадёт уже не та функция, которую вы ожидали передать.

Правильно так:

queueMicrotask(() => {
  console.log("Привет");
});

Думать, что queueMicrotask() возвращает промис

const result = queueMicrotask(() => {
  console.log("run");
});

queueMicrotask() не возвращает Promise. У него другая задача: просто поставить функцию в очередь микрозадач.

Перегружать очередь микрозадач

function repeat() {
  queueMicrotask(repeat);
}

repeat();

Такой код может надолго занять цикл событий и помешать таймерам, рендерингу и другой работе. Для тяжёлых или бесконечных повторов queueMicrotask() подходит плохо.

Быстрая памятка

  • queueMicrotask() ставит функцию в очередь микрозадач
  • колбэк выполняется после текущего синхронного кода
  • микрозадача выполняется раньше setTimeout(..., 0)
  • метод принимает только функцию
  • задержку в миллисекундах указать нельзя
  • метод ничего полезного не возвращает
  • несколько микрозадач выполняются в порядке добавления
  • слишком большое количество микрозадач может задерживать остальную работу цикла событий

Коротко

queueMicrotask() в JavaScript нужен, чтобы поставить функцию в очередь микрозадач. Главное, что стоит запомнить: колбэк выполняется не сразу, а после текущего синхронного кода, при этом раньше таймеров вроде setTimeout(..., 0), метод не возвращает промис и особенно полезен там, где нужно очень быстро отложить небольшой кусок логики без перехода в более позднюю очередь задач.