Иногда в JavaScript кажется, что код выполняется “не по порядку”: сначала написано одно, а в консоли появляется другое. Это нормально - так работает асинхронность.
Асинхронность нужна, чтобы браузер не “зависал”, пока идет длительная операция: запрос в сеть, ожидание таймера, чтение файла, работа с пользовательским вводом.
1) Синхронный код: строго сверху вниз
Синхронный код выполняется по порядку, строка за строкой.
console.log("A");
console.log("B");
console.log("C");
Результат:
A
B
C
2) Почему нельзя просто “ждать” в JavaScript
JavaScript в браузере обычно выполняется в одном потоке: пока выполняется код, страница не может нормально реагировать на клики, прокрутку, ввод.
Если сделать тяжелую работу синхронно, интерфейс “замрет”.
console.log("Start");
let sum = 0;
for (let i = 0; i < 1_000_000_000; i += 1) {
sum += i;
}
console.log("End");
Пока идет цикл, страница может подвиснуть.
3) Асинхронные операции: результат будет позже
Некоторые действия запускаются сейчас, а результат приходит позже:
- таймеры (
setTimeout) - сетевые запросы (
fetch) - события (клик, input)
- работа с файлами
Такие операции обычно принимают функцию, которая выполнится, когда результат готов.
Пример с setTimeout
console.log("A");
setTimeout(() => {
console.log("B (timeout)");
}, 0);
console.log("C");
Вывод будет:
A
C
B (timeout)
Почему так: setTimeout(..., 0) не означает “сразу”. Это означает “выполнить после того, как завершится текущий синхронный код и очередь позволит”.
4) Что происходит внутри: очередь задач и event loop
Упрощенная модель:
- синхронный код выполняется прямо сейчас
- асинхронные коллбеки попадают в очередь
- когда основной код закончился, event loop берет задачи из очереди и выполняет
Из-за этого и возникает эффект “не по порядку”.
5) Callback как идея: выполнить позже
Обработчик в setTimeout - это callback.
function logLater(text) {
setTimeout(() => {
console.log(text);
}, 500);
}
logLater("Hello");
Смысл: “сделать сейчас, а завершить позже”.
6) Promises: удобный формат “результат будет потом”
Многие современные API (например fetch) возвращают Promise - объект, который представляет будущий результат.
Мини-пример:
const promise = Promise.resolve("OK");
promise.then((value) => {
console.log(value);
});
console.log("After");
Сначала выведется “After”, затем “OK” (Promise тоже выполняется не в середине синхронного кода).
7) Почему это важно для реального кода
Когда внутри есть запрос или таймер, результат нельзя использовать “сразу” после запуска.
Неправильная логика:
let data;
fetch("/api/data").then((response) => response.json()).then((json) => {
data = json;
});
console.log(data);
console.log(data) сработает раньше, чем придет ответ, поэтому там будет undefined.
Правильный подход: работать с данными внутри then или через async/await (это будет следующим шагом в модуле).
fetch("/api/data")
.then((response) => response.json())
.then((json) => {
console.log(json);
});
8) Частые причины “не по порядку”
setTimeout,setIntervalfetchи любые сетевые запросы- события DOM (клик, input)
- Promise и
then asyncфункции (внутри они тоже используют Promise)
Что стоит запомнить
- Синхронный код идет сверху вниз.
- Асинхронные операции завершаются позже и ставят продолжение в очередь.
setTimeout(..., 0)не выполняется мгновенно, он ждет завершения текущего кода.- Promise - стандартный способ работать с будущим результатом.
- Если результат приходит позже, его нельзя использовать “сразу после запуска” - логика должна быть внутри
thenили черезasync/await.