Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про JavaScript: callback, asynchronous, race condition, closure, JavaScript

Почему код с callback может работать некорректно в асинхронном контексте?

Вопрос проверяет понимание проблем асинхронного выполнения и состояния гонки (race condition) при использовании callback-функций.

Короткий ответ

Callback-функция может работать некорректно в асинхронном контексте из-за замыканий и состояния гонки. Если callback использует переменные из внешней области видимости, их значение может измениться до вызова callback, так как асинхронная операция завершится позже. Например, в цикле с асинхронными вызовами все callback могут получить одно и то же значение последней итерации. Это происходит из-за того, что переменная цикла изменяется синхронно, а callback вызываются асинхронно.

Длинный ответ

Callback-функции — это функции, передаваемые в качестве аргументов другим функциям для выполнения после завершения какой-либо операции, часто асинхронной. Основная проблема возникает, когда callback захватывает (замыкает) переменные из внешней лексической области видимости, и эти переменные изменяются до момента вызова callback.

Проблема с замыканиями и циклом

Классический пример — использование цикла for с асинхронными операциями внутри. Переменная цикла объявлена с помощью var, которая имеет функциональную область видимости. К моменту вызова callback-функций цикл уже завершён, и все callback ссылаются на одно и то же конечное значение переменной.

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // Всегда выводит 3
  }, 100);
}

Здесь setTimeout — асинхронная функция, её callback выполняется после завершения цикла. Переменная i к тому моменту равна 3, и все три callback выводят это значение.

Состояние гонки (Race Condition)

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

let balance = 100;

function asyncAdd(amount, callback) {
  setTimeout(() => {
    balance += amount;
    callback();
  }, Math.random() * 100);
}

asyncAdd(10, () => console.log('Balance after first:', balance));
asyncAdd(20, () => console.log('Balance after second:', balance));
// Порядок вывода и итоговое значение balance непредсказуемы.

Решение проблем

Чтобы избежать проблем с замыканиями, можно:

  • Использовать let или const для блочной области видимости (в современном JavaScript).
  • Создавать новую область видимости для каждой итерации, например, с помощью IIFE (Immediately Invoked Function Expression).
  • Передавать значение как параметр функции.
// Решение с let
for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // Выводит 0, 1, 2
  }, 100);
}

// Решение с IIFE
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j); // Выводит 0, 1, 2
    }, 100);
  })(i);
}

Для борьбы с состоянием гонки следует использовать механизмы синхронизации, такие как промисы с async/await, очереди или мьютексы, чтобы гарантировать порядок выполнения или атомарность операций.

Вывод: Callback могут работать некорректно в асинхронном контексте из-за изменения захваченных переменных и состояний гонки. Для написания надёжного асинхронного кода важно понимать замыкания и использовать современные подходы (промисы, async/await) или тщательно изолировать данные для каждого callback.

Уровень

  • Рейтинг:

    4

  • Сложность:

    3

Навыки

  • JavaScript

    JavaScript

Ключевые слова

#callback

#asynchronous

#race condition

#closure

#JavaScript

Подпишись на React Developer в телеграм