Перейти к основному содержимому

Асинхронное программирование в JavaScript

Разработчику Архитектору

JavaScript Runtime — интерактивно

Ваш короткий скелет можно читать так:

Script.js
|
+--> V8 Engine (исполняет синхронный JS, держит Call Stack, запускает Garbage Collection)
|
+--> Web/Host APIs (таймеры, сеть, события, I/O вне движка)
|
+--> Event Loop
|- сначала очищает Microtask Queue (Promise/await/queueMicrotask)
|- потом берёт одну задачу из Callback Queue (macrotask)
|- повторяет цикл, пока приложение живо

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Итог — JavaScript остаётся однопоточным по выполнению кода в стеке, но приложение ведёт себя асинхронно и non-blocking благодаря очередям, Event Loop и внешним API среды.

Play ITЗагрузка интерактивного демо…


Асинхронность в JavaScript

Реализация асинхронности в JS

JavaScript использует механизм асинхронности, обеспечивая одновременное выполнение нескольких задач "параллельно" без ожидания окончания предыдущих в очереди. Важно подчеркнуть, что JS однопоточный язык, а параллелизм имитируется благодаря event loop и асинхронным API.

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

JavaScript - однопоточный язык программирования. Это значит, что в каждый момент времени он может выполнять только одну инструкцию. Представьте, что у вас есть одна линия конвейера, по которой проходит одна деталь за раз. Никакого параллельного выполнения, как в многопоточных языках (например, Java или C#), в чистом JS нет. Инструкция 1, потом Инструкция 2. И пока Инструкция 1 не выполнится, поток блокируется и мы не переходим к Инструкции 2.

Но если JS выполняет только одну задачу за раз — как тогда он может загружать данные с сервера, реагировать на клики, устанавливать таймеры, читать файлы — и при этом не подвисать? Ответ - за счёт асинхронной модели выполнения, построенной на Event Loop и асинхронных API браузера или среды выполнения (например, Node.js).

Таким образом, здесь асинхронность не является многопоточностью.

Когда вы пишете:

console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");

Разбор:

  • console.log("1") выполняется синхронно и сразу печатает 1.
  • setTimeout(..., 0) регистрирует callback в host API и не блокирует текущий стек вызовов.
  • Задержка 0 означает "как можно раньше после текущего тика", а не "выполнить немедленно".
  • console.log("3") также синхронный, поэтому отрабатывает до callback таймера.
  • Callback () => console.log("2") попадёт в очередь макрозадач и выполнится только после опустошения стека.
  • Поэтому итоговый порядок вывода — 1, 3, 2.
  • Фрагмент иллюстрирует базовый принцип Event Loop и различие между синхронным кодом и отложенными задачами.

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

То можете ожидать из природы интерпретируемости JS, что выйдет результат 1, 2, 3, так как задержка в setTimeout указана 0 миллисекунд. Но вывод будет иным - 1, 3, 2. Почему? Потому что setTimeout — асинхронная операция, и она не блокирует выполнение кода. Вместо этого она передаёт свою функцию-коллбэк в внешнюю систему (браузер или Node.js), которая запускает таймер в фоне, а сам JS продолжает работать дальше.

Именно Event Loop отвечает за то, когда и в каком порядке эти отложенные функции будут вызваны — после завершения текущей синхронной работы.


Архитектура выполнения кода в JS

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

  1. Стек вызовов (Call Stack) - это стек, в котором хранятся функции, которые сейчас выполняются. JS работает по принципу LIFO (Last In, First Out) — последняя добавленная функция выполняется первой.
function greet() {
console.log("Привет!");
}
greet(); // → добавляется в стек, выполняется, удаляется

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Когда greet() вызывается, она попадает в стек. После завершения — удаляется.

  1. Web APIs (или Host APIs) - это внешние среды, предоставляемые браузером или Node.js, такие как:
    • setTimeout, setInterval
    • fetch, XMLHttpRequest
    • addEventListener
    • requestAnimationFrame
    • Работа с DOM, файлами и т.д.

Когда вы вызываете setTimeout, JS не выполняет таймер сам. Он передаёт задачу в Web API, которое начинает отсчёт времени в фоне, независимо от JS. Поэтому и так получается - 1, 3, 2.

  1. Очереди: Task Queue (Macrotasks) и Microtask Queue. То есть, бывает два вида очередей - микрозадачи и макрозадачи. Когда асинхронная операция завершается, её коллбэк не выполняется сразу. Он помещается в одну из двух очередей:

Микрозадачи (очередь микрозадач) - высокий приоритет, и сюда попадают .then, .catch, .finally от промисов (Promise), queueMicrotask(), MutationObserver (отслеживание изменений DOM). Все микрозадачи выполняются до следующей макрозадачи.

Макрозадачи (очередь макрозадач) - низкий приоритет, и сюда попадают setTimeout, setInterval, setImmediate (в Node.js), события (click, input и т.д.), I/O операции. Одна задача из этой очереди выполняется за один цикл Event Loop.

  1. Event Loop (Цикл событий) - механизм, который постоянно проверяет:
    • пуст ли стек вызовов?
    • есть ли микрозадачи? Если да, то выполняет все из очереди микрозадач;
    • есть ли макрозадачи? Если да, то выполняет одну из очереди макрозадач;
    • возвращается к шагу (проверка стека вызовов).

Такой вот цикл - это работает по кругу снова и снова. Event Loop — это не поток и не таймер. Это просто цикл, который проверяет стек и очереди.


Микрозадачи и queueMicrotask

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

Ключевые источники микрозадач:

  • обработчики Promise.then, Promise.catch, Promise.finally;
  • продолжение async-функции после await;
  • queueMicrotask;
  • MutationObserver.

Базовое правило цикла:

  1. выполняется текущий синхронный код;
  2. полностью очищается очередь микрозадач;
  3. берётся следующая макрозадача (setTimeout, событие, I/O);
  4. после неё снова очищается очередь микрозадач.

Из-за этого микрозадачи всегда выполняются раньше таймеров и других макрозадач.


Зачем нужен queueMicrotask

queueMicrotask(callback) планирует callback в очередь микрозадач без создания промиса. Это полезно, когда нужно:

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

Пример — нормализация порядка событий:

function emitReady(handler, fromCache) {
if (fromCache) {
// Даже при "мгновенном" результате остаёмся в асинхронном контракте.
queueMicrotask(() => handler("cache"));
return;
}

fetch("/api/data")
.then(() => handler("network"));
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Идея примера — обе ветки вызывают handler асинхронно через очередь микрозадач, поэтому поведение предсказуемо.


queueMicrotask и Promise.resolve().then

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

  • queueMicrotask прямо выражает намерение "выполнить как микрозадачу";
  • Promise.resolve().then добавляет промежуточный промис и чаще используется в цепочках промисов.

Практическое правило:

  • если нужна именно микрозадача как механизм планирования — берите queueMicrotask;
  • если строите pipeline промисов с результатом и ошибками — берите Promise.

Порядок выполнения на примере

console.log("A");

setTimeout(() => console.log("B (macrotask)"), 0);

queueMicrotask(() => console.log("C (microtask)"));

Promise.resolve().then(() => console.log("D (microtask via Promise)"));

console.log("E");

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Результат:

A
E
C (microtask)
D (microtask via Promise)
B (macrotask)

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Осторожно с "переполнением" очереди микрозадач

Если микрозадача в бесконечном цикле добавляет новую микрозадачу, Event Loop продолжает очищать очередь микрозадач и откладывает макрозадачи. В браузере это выглядит как "подвисший" интерфейс.

function spin() {
queueMicrotask(spin);
}

// Никогда так не делайте в реальном коде.
// spin();

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Для длинных серий лучше периодически уступать управление через макрозадачу (setTimeout(..., 0)) или делить работу на батчи.


Когда выбирать microtask и macrotask

  • Микрозадача (queueMicrotask, Promise.then) — "сразу после текущего стека", до таймеров и событий.
  • Макрозадача (setTimeout, setInterval, события, I/O) — когда нужно явно отложить выполнение на следующий тик цикла событий.

Примеры асинхронного кода

Пример.

console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

Разбор:

  • Первая и последняя строки (console.log("1"), console.log("4")) исполняются синхронно в текущем стеке.
  • setTimeout ставит callback в макрозадачи, поэтому этот обработчик ждёт следующего шага Event Loop.
  • Promise.resolve().then(...) формирует микрозадачу, у которой приоритет выше, чем у макрозадач.
  • После завершения синхронного кода движок сначала полностью очищает очередь микрозадач.
  • Поэтому console.log("3") выполняется раньше таймера, даже при 0 миллисекундах.
  • Лишь потом запускается callback из setTimeout, который печатает 2.
  • Реальный порядок вывода — 1, 4, 3, 2, и это ключ к пониманию Promise + таймеров.

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Код выглядит простым, и давайте глянем, как всё работает:

  1. console.log("1") — синхронный вызов → выполняется сразу → выводит 1;
  2. setTimeout(...) — передаёт коллбэк в Web API, которое начинает таймер (0 мс). Коллбэк ещё не в очереди, но будет помещён в очередь макрозадач, когда таймер сработает;
  3. Promise.resolve().then(...) — промис уже выполнен, .then помещает коллбэк в очередь микрозадач;
  4. console.log("4") — синхронный вызов → выполняется → выводит 4;
  5. Стек вызовов пуст → Event Loop начинает работать;
  6. Проверка очереди микрозадач → есть одна задача (console.log("3")) → выполняется → выводит 3;
  7. Очередь микрозадач пуста → Event Loop переходит к очереди макрозадач;
  8. Очередь макрозадач → есть setTimeout → выполняется → выводит 2.

Итого, в результате мы увидим 1, 4, 3, 2.

Важно — даже если setTimeout установлен на 0, он всегда ждёт, пока все микрозадачи будут выполнены. Промисы всегда приоритетнее таймеров.

Почему промисы в микрозадачах, а setTimeout — в макрозадачах? Потому что промисы — это часть логики программы, и их обработка должна быть немедленной и предсказуемой. Если вы используете .then(), вы ожидаете, что он выполнится как только промис разрешится, без задержек. В то время как setTimeout — это планировщик, и его задача — отложить выполнение на следующий "кадр" или цикл.

Пример:

fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Что происходит:

  1. fetch — асинхронная операция → передаётся в Web API (браузер).
  2. JS не ждёт ответа → продолжает выполнять следующие строки.
  3. Когда сервер ответит, Web API помещает коллбэк из .then в очередь микрозадач.
  4. Как только стек освободится, Event Loop выполнит этот коллбэк.
  5. Таким образом, основной поток не блокируется, и страница остаётся отзывчивой.

А что такое асинхронная функция? Как можно понять, это будет как-то так:

async function load() {
console.log("A");
await fetch("/api"); // → ожидание
console.log("B");
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь создаётся асинхронная функция load(), которая логирует A, потом выполняет fetch по адресу /api, ожидает и логирует B.

Функция становится асинхронной потому что:

  1. она помечена как async - всегда возвращает промис;
  2. внутри неё можно использовать await - логика приостанавливается на промисе;
  3. после await происходит продолжение функции - это микрозадача.
async function f() {
await 1; // await с примитивом → промис, разрешённый сразу
console.log("after await");
}
f();
console.log("sync");

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь создаётся асинхронная функция f(), которая ожидает 1 - это промис, который ожидает примитив, потом логирует "after await". А глобально происходит вызов f(), затем логирование sync. Вывод, как можно понять, будет сначала sync, потом after await. Потому что await это обёрнутый промис .then(), который является микрозадачей. А микрозадачи всегда выполняются после синхронного кода.

Итого, сначала синхронный код, потом все микрозадачи, потом одна макрозадача. Коллбэки сразу не вызываются, а попадают в очереди и ждут. А acync/await называют синтаксическим сахаром, потому что работа выполняется на том же механизме - промисы являются микрозадачами.

И если бы вам нужно было вывести и выполнить всё по порядку без какого-то ожидания…вы бы просто делали синхронный код. Да, у вас будет 1, 2, 3, к примеру, но если вывод "2" требует запроса к серверу, который может грузиться или считать какое-то время…то у вас программа остановится на этом месте и будет ждать, пока сервер соизволит ответить. И JavaScript всё ещё остаётся однопоточным, и делает вид, что выполняет несколько задач параллельно, благодаря Event Loop.


Механизмы асинхронности

Механизмы асинхронности:

  1. Callback – функции, вызываемые после завершения операции.
  2. Promises (Промисы) – более удобная альтернатива колбэкам.
  3. Async/Await – синтаксический сахар над промисами.

Разберём их более детально.


Callback

Callback – фундаментальный паттерн, когда функция передаётся как аргумент и выполняется после завершения асинхронной операции. То есть, callback-функции – это функции, которые передаются в другую функцию и выполняются после какого-то события.

Представим, что мы просим друга сделать что-то и говорим ему: "Вот инструкция (функция). Выполни её, когда закончишь своё дело". Мы – JS-код, а друг – асинхронная функция:

сходи в магазинасинхронная операция
а когда вернёшься – позвони мнеcallback

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

Давайте разберём шаблон создания функции с коллбеком:

function имяФункции(параметры, callback) {
// какая-то асинхронная операция
// например: setTimeout, fetch, чтение файла и т.д.

// когда операция завершится:
if (успех) {
callback(null, результат); // первый аргумент — ошибка (null если успех)
} else {
callback(ошибка, null); // ошибка в первом аргументе
}
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Стоит сразу отметить, что в Node.js и многих библиотеках используется ошибка первым аргументом (error-first callback). Это стандарт. Но давайте разбирать вышеприведенный шаблон.

Здесь, как мы видим, происходит обычное объявление обычной функции через function имяФункции(). К примеру, это может быть function fetchData(url, callback).

Далее мы видим, что в параметрах функции есть callback. Это один из параметров, в который передадут функцию. То есть, callback это просто имя параметра, можно cb, onDone, handler, да что угодно.

И мы видим, что в тексте есть два вызова callback:

  • callback(null, data) - вызов коллбека с данными, к примеру при успехе будет callback(null, "данные");
  • callback(error, null) - вызов при ошибке, к примеру, callback("Ошибка сети", null).

Сложно? Технически, получается, вызывая эту функцию, мы просто в том месте, где callback, передаем функцию. К примеру, имяФункции(аргумент, имяФункцииКоллбека).

Давайте ещё пример:

function loadData(callback) {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
callback(null, "Данные загружены!");
} else {
callback("Ошибка загрузки", null);
}
}, 1000);
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь абсолютный аналог. callback - просто имя параметра, которое можете назвать как угодно, но мы будем подразумевать, что будем вызывать его. А в loadData(...) мы передаём функцию как аргумент — это и есть коллбэк.

Что следует запомнить:

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

Погнали дальше.

function loadData(callback) { // функция принимает callback
setTimeout(() => { //происходит эмуляция асинхронности
callback("Данные загружены!"); //через 1 секунду вызываем callback с результатом
}, 1000);
}

loadData((result) => { // передаём callback-функцию
console.log(result); // она сработает через 1 сек: "Данные загружены!"
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

В вышеприведённом примере это работает так, пошагово:

  1. loadData – это функция, которая ждёт callback (друг, ждущий инструкцию);
  2. Внутри loadData есть setTimeout – он имитирует долгую операцию (например, запрос к серверу).
  3. Через 1 секунду setTimeout вызывает callback("Данные загружены!").
  4. Мы передаём анонимную функцию (result) =>{ console.log(result); } как callback.
  5. Когда setTimeout срабатывает – callback выполняется, и в консоль выводится результат.

Callback применяется для загрузок данных с сервера, чтения файлов, таймеров (setTimeout, setInterval), обработки событий, или работы с API.

Пример (setTimeout):

setTimeout(() => {
console.log("Прошло 2 секунды!");
}, 2000);

() => { console.log() } – это callback.

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Он выполнится после того, как пройдёт 2 секунды.

Пример (AJAX):

function fetchData(url, callback) {
fetch(url)
.then(response => response.json())
.then(data => callback(data)) // Передаём данные в callback
.catch(error => console.error(error));
}

fetchData("https://api.example.com/data", (data) => {
console.log("Получены данные:", data);
});

(data) => { console.log(data); } – callback, который сработает после загрузки данных.

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Пример (обработчик клика):

button.addEventListener("click", () => {
console.log("Кнопка нажата!");
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Вся стрелочная функция – это callback.

Она выполнится после клика на кнопку.

Таким образом, callback – это функция, которая выполнится после какого-то события.


Promise

Promises (промисы) – объекты-обёртки для асинхронных операций, которые:

  • ждут завершения (pending);
  • по завершении получают fulfilled или rejected (возвращают результат или ошибку);
  • позволяют цеплять обработчики (then/catch).
Интерактивно — fetch и цепочка .then

Пошагово: как fetch не блокирует UI, куда попадают .then и почему console.log('B') выполняется раньше ответа сервера. То же демо — в живых примерах асинхронности.

Play ITЗагрузка интерактивного демо…

Pending → Fullfulled с результатом или Rejected с ошибкой.

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на пошаговом выполнении кода.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Promise (от английского) – "обещание" JavaScript сделать что-то асинхронное и сообщить результат — успех (fulfilled), ошибка (rejected), ожидание (pending) – ещё выполняется.

image-6.png

Итого, промисом является некий объект, представляющий будущий результат асинхронной операции, который может быть в одном из трёх состояний - ожидание, успех или ошибка. Давайте подготовим шаблон для создания промиса и разберём его:

const имяПеременной = new Promise((resolve, reject) => {
// асинхронная операция (таймер, запрос, чтение файла...)

if (операцияУспешна) {
resolve(результат); // промис переходит в fulfilled
} else {
reject(ошибка); // промис переходит в rejected
}
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь мы создаём переменную как новый объект Promise. А resolve и reject — это функции, которые вы не создаёте, а получаете как параметры от new Promise.

new Promise(...) означает создание объекта-обещания.

(resolve, reject) означают функции, которые будут завершать это обещание.

resolve(данные) означает "всё хорошо, вот результат", к примеру, resolve(user).

reject(ошибка) означает "всё плохо, вот причина", к примеру, reject("Сеть недоступна").

Пример:

const getUser = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve({ id: 1, name: "Алекс" });
} else {
reject(new Error("Не удалось загрузить пользователя"));
}
}, 1000);
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь создаётся переменная getUser, выполняется операция, и при успехе передаётся результат в параметрах resolve, иначе - reject и ошибка как новый объект с текстом.

Использование промиса выполняется тремя методами - .then, .catch, .finally. Шаблонно можно сделать так:

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.

  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.

  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.

  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.

  • В примере акцент сделан на работе с асинхронным выполнением.

  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.

  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.

  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

    • .then(result => { ... }) здесь вызывается при успехе (resolve), может вернуть новый промис или значение. Не обязателен, но без него смысла в промисе нет.
    • .catch(error => { ... }) вызывается при ошибке (reject), ловит ошибки из resolve, reject или предыдущих .then, и рекомендуется, чтобы ошибки не потерялись. Без него ошибки "проглатываются", и вы не узнаете, что что-то пошло не так. Можно писать только .then, но если произойдёт ошибка, она не будет обработана, и в консоли появится unhandled promise rejection.
    • .finally(() => { ... }) выполняется всегда, после .then или .catch, не получает аргументов, и является опциональным.

.then и .catch возвращают новый промис, поэтому можно строить цепочки. Каждый .then может возвращать промис — тогда следующий .then дождётся его.

Пример:

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Здесь мы указывает что если user то печатаем одно, если id, то другое. А в любом случае выводим Готово!.

Как мы ранее усвоили, .then и .catch — это микрозадачи, которые выполняются после синхронного кода, но до setTimeout.

Чем промис отличается от колбэка?

Колбэк (Callback)Промис (Promise)
Функция, которая вызывается после операции.Объект, который представляет будущий результат и сам управляет своим состоянием.
Мы даём номер телефона (колбэк) курьеру и говорим: "Позвони, как довезёшь". И если курьер не позвонит – пицца зависнет.Нам дают чек на заказ (промис), у которого есть статусы. И мы точно знаем, что пицца не потерялась, чек (промис) – гарантия доставки. Мы можем заниматься своими делами. Пицца доставлена – можем кушать. Сгорела (rejected) – мне вернут деньги.

Промисы дают набор статических методов (Promise API) для разных сценариев. Удобно запомнить их как режимы ожидания.

МетодЧто делаетКогда применять
Promise.resolve(value)Возвращает уже успешный промисНужен единый контракт функции "всегда вернуть промис"
Promise.reject(reason)Возвращает уже отклонённый промисНужен ранний выход с ошибкой
Promise.all(iterable)Ждёт успех всех промисов, падает на первой ошибкеВсе результаты обязательны
Promise.allSettled(iterable)Ждёт завершения всех промисов, не падает из-за отдельных ошибокПакетные операции, нужен полный отчёт
Promise.any(iterable)Возвращает первый успешный результатЗеркала, fallback-источники
Promise.race(iterable)Возвращает первый завершившийся промис (успех или ошибка)Таймауты и гонки
Promise.withResolvers()Возвращает { promise, resolve, reject }Управляемый промис: завершение снаружи executor'а (ES2024)

Управляемый Promise: Promise.withResolvers()

Иногда resolve и reject нужны вне колбэка new Promise — когда завершение привязано к событию, колбэку старого API или коду, который executor не видит (очередь задач, WebSocket, addEventListener, мост между callback- и async/await-стилем).

Раньше для этого писали обходной шаблон:

let resolve;
const promise = new Promise((res) => {
resolve = res;
});
// позже: resolve(value)

Работает, но появляются лишние переменные, нет reject "из коробки", в TypeScript тип resolve часто остаётся неточным.

С ES2024 есть статический метод Promise.withResolvers() — он сразу возвращает готовый промис и функции управления:

const { promise, resolve, reject } = Promise.withResolvers();

setTimeout(() => resolve("done"), 1000);

async function main() {
console.log(await promise); // done
}

await promise здесь — внутри async-функции или в ES-модуле с top-level await. До вызова resolve/reject промис в состоянии pending.

Типичные сценарии:

  • ожидание первого сообщения WebSocket или сигнала "готов" от внешней библиотеки;
  • очередь: следующий dequeue ждёт, пока предыдущая операция вызовет resolve;
  • обёртка над API на колбэках (fs, старые SDK) — внутри resolve/reject, снаружи await promise.
Поддержка среды

Метод в спецификации ES2024; в современных браузерах и Node.js 22+. Перед использованием проверьте runtime (MDN, node --version). Справочная таблица Promise API — 251.md (раздел Promise).

Promise.allSettled() дожидается завершения каждого промиса — успешного или с ошибкой. Результат у каждого элемента имеет один из двух видов:

  • { status: 'fulfilled', value }
  • { status: 'rejected', reason }

Это удобно для пакетных действий, где нужен итог по каждому элементу и полный отчёт по результатам. См. также HTTP batch API и Пакетная работа с данными.

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Ещё три практических шаблона:

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Давайте пробежимся ещё раз по промису.

Как создать промис?

<промис> = new Promise((resolve, reject) => {
if (<условие успеха>) {
resolve(<значение>);
} else {
reject(<ошибка>);
}
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
const myPromise = new Promise((resolve, reject) => {
// Симуляция асинхронной операции (например, запрос к серверу, чтение файла, таймер)
const isSuccess = /* Логика проверки успеха или ошибки */;
if (isSuccess) {
resolve(/* Результат успешной операции */);
} else {
reject(/* Причина ошибки */);
}
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Как использовать промис?

Вообще, использование подразумевает обработку результата промиса:

<промис>.then(<обработчик успеха>).catch(<обработчик ошибки>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Разбирая пример с пиццей, можно сделать это кодом в два этапа:

  1. Создание промиса:
const pizzaPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const isSuccess = Math.random() > 0.5; // 50% шанс успеха
if (isSuccess) {
resolve("Пицца доставлена!"); // Успех
} else {
reject("Пицца сгорела в печи!"); // Ошибка
}
}, 2000); // Через 2 секунды
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
  1. Использование промиса:
pizzaPromise
.then((result) => {
console.log(result); // "Пицца доставлена!"
})
.catch((error) => {
console.error(error); // "Пицца сгорела в печи!"
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
  1. new Promise создаёт объект с двумя функциями:
    • resolve() – вызывается при успехе;
    • reject() – вызывается при ошибке.
  2. Через 2 секунды "кухня" (setTimeout) сообщает результат.
  3. .then() ловит успех, .catch() – ошибку.

Цепочка асинхронных операций будет выглядеть так:

<промис>
.then(<обработчик 1>)
.then(<обработчик 2>)
.catch(<обработчик ошибки>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Допустимо параллельное выполнение нескольких промисов:

Promise.all([<промис1>, <промис2>, ..., <промисN>])
.then(<обработчик всех результатов>)
.catch(<обработчик первой ошибки>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

В случае, если нас интересует ожидание первого завершенного промиса:

Promise.race([<промис1>, <промис2>, ..., <промисN>])
.then(<обработчик первого результата>)
.catch(<обработчик первой ошибки>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Дополнительно

Отмена зависших запросов (AbortController) и односторонний поток с сервера (EventSource) — в главе Отмена запросов и поток событий с сервера.


AJAX, XHR и модели Comet

AJAX (Asynchronous JavaScript and XML) — подход к веб-интерфейсу, при котором браузер запрашивает данные у сервера в фоне и обновляет часть страницы без полной перезагрузки. Термин закрепился в 2005 году (Джесси Джеймс Гарретт, статья Ajax: A New Approach to Web Applications). "XML" в названии историческое: сегодня чаще передают JSON, иногда HTML-фрагмент или текст.

Технический кирпич эпохи AJAX — XMLHttpRequest (XHR). Им задают метод, URL, заголовки и обработчики onload / onerror; ответ разбирают вручную. XHR до сих пор встречается в legacy, но в новом коде предпочитают fetch (возвращает Promise, удобнее с async/await).

Эволюция подхода:

ЭтапИнструментКомментарий
1<iframe>Скрытый фрейм загружает URL без перерисовки всей страницы — грубо, редко сегодня
2XMLHttpRequestКлассический AJAX; колбэки, ручной разбор ответа
3fetchPromise, async/await, стандарт Fetch API

Пошаговые примеры fetch (GET, POST, таймаут, retry) и проверка API через curl — curl / fetch — API-запросы. Галерея fetch и axios с разбором — Fetch / axios — типовые запросы. В React тот же fetch внутри useEffectПервая программа на React и галерея компонентов.

Минимальный XHR (для чтения старого кода):

Код ITЗагрузка примера кода…

Разбор:

  • open(method, url) задаёт метод и адрес до send().
  • responseType = 'json' просит браузер распарсить JSON (в старом коде часто вызывали JSON.parse(req.responseText)).
  • Ошибки сети и HTTP нужно проверять вручную — как и у fetch, статус 404 сам по себе исключение не бросает.
МеханизмКто инициирует обменТипичное применение
Классическая формабраузер → сервер, полная перезагрузкапростые сайты
AJAX / fetchбраузер → сервер по действию пользователяавтодополнение, сохранение черновика
Comet (long polling, streaming)сервер "подталкивает" данные по HTTPранние чаты, биржевые ленты
SSE (EventSource)сервер → клиент, один потокуведомления, прогресс задачи
WebSocketобе сторонычат, совместное редактирование

Comet — собирательное имя для веб-приложений, где соединение HTTP держится открытым или периодически переоткрывается, чтобы сервер отправил данные без нового клика. На практике это long polling, "висящий" ответ или поток. Современный стандартный вариант push в одну сторону — Server-Sent Events; для двустороннего канала — WebSocket.

Исторический контекст — в истории JavaScript; веб-архитектура — в применении в вебе.


fetch API

Хороший пример использования промисов - fetch API, современный встроенный в браузер (и Node.js с определённых версий) способ делать HTTP-запросы к серверам. Он возвращает промис. Сама по себе функция fetch() используется для того, чтобы получить данные с сайта, отправить форму, загрузить форму или взаимодействовать с API. Это замена старому способу — XMLHttpRequest.

Базовый синтаксис:

fetch(url, options)
.then(response => {
// Обработка ответа
})
.catch(error => {
// Обработка ошибок сети
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

url — адрес, куда отправляется запрос.

options — необязательный объект: метод (GET, POST и т.д.), заголовки, тело запроса и др.

fetch(...) — отправляет запрос по указанному адресу. Сервер отвечает (например, списком пользователей).

.then(response => response.json()) — превращает ответ в данные (обычно JSON).

Смысл в том, что сразу данные мы не получаем - нужно дождаться, пока промис выполнится. Когда fetch получает ответ от сервера, он передаёт его в .then() как объект response. У этого объекта есть полезные свойства и методы:

  • response.ok - true, если статус 200–299 (успех), иначе false;
  • response.status - код ответа;
  • response.statusText - текст статуса;
  • response.headers - заголовки ответа;
  • response.url - URL, по которому был получен ответ.
  • response.json() - парсит ответ как JSON;
  • response.text() - читает ответ как обычный текст;
  • response.blob() - для файлов (картинок, PDF и тд);
  • response.formData() - для форм.

fetch НЕ выбрасывает ошибку при HTTP-ошибках (404, 500 и т.п.). Он считает, что запрос успешно дошёл до сервера, а значит — технически всё ок. Поэтому всегда проверяйте response.ok, если важен успех операции:

fetch('/api/user')
.then(response => {
if (!response.ok) {
throw new Error(`Ошибка ${response.status}`);
}
return response.json();
})
.then(user => console.log(user));

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

По умолчанию fetch делает GET-запрос (получает данные). Чтобы отправить данные — нужно указать параметры:

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

fetch() используется повсюду — получение данных в React/Vue/Angular, отправка форм без перезагрузки страницы, работа с внешними API. В старых браузерах вроде IE он не поддерживается, встроенного timeout нет, прогресс загрузки — через ReadableStream.

URL и query-параметры

Класс URL разбирает адрес; URLSearchParams — строку параметров после ?:

const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'node.js');
url.searchParams.set('page', '2');
console.log(url.toString());
// https://api.example.com/search?q=node.js&page=2

const params = new URLSearchParams({ tag: 'js', limit: '10' });
fetch(`/api/posts?${params}`);

FormData — формы и файлы

Для отправки полей формы и файлов без ручного JSON:

const formData = new FormData();
formData.append('title', 'Заметка');
formData.append('file', fileInput.files[0]);

const response = await fetch('/api/upload', {
method: 'POST',
body: formData
// Content-Type браузер выставит сам с boundary
});

Загрузка файлов в браузере — чтение файлов. Отмена запроса — AbortController ниже и глава 37.

Важно про cookies и авторизацию:

  • для same-origin запросов браузер отправляет cookies по умолчанию (credentials: 'same-origin');
  • для cross-origin запросов cookies/HTTP-auth отправятся только при credentials: 'include' и корректной CORS-настройке сервера.

Минимальный надёжный шаблон fetch:

async function loadUser() {
const response = await fetch('/api/user', {
credentials: 'same-origin'
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}

return response.json();
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Таймаут и отмена делаются через AbortController:

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Паттерны использования промисов

Паттерны использования промисов:

  1. Цепочка (chaining) – каждый .then возвращает новый промис:
fetch("/api/user") // Запрашиваем пользователя
.then((response) => response.json()) // Преобразуем в JSON
.then((user) => fetch(`/api/posts?userId=${user.id}`)) // Запрашиваем посты
.then((response) => response.json()) // Ещё раз JSON
.then((posts) => console.log("Посты пользователя:", posts))
.catch((err) => console.error("Ошибка:", err)); // Ловим все ошибки в цепочке

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
  1. Параллельное выполнение – запускаются несколько промисов одновременно и ждём все результаты. Если все промисы успешны - .then() получит массив результатов. Если хотя бы один упадёт – сработает .catch.
Promise.all([
fetch("/api/users"),
fetch("/api/posts"),
fetch("/api/comments")
])
.then(([users, posts, comments]) => {
console.log("Все данные загружены:", { users, posts, comments });
})
.catch((err) => {
console.error("Один из запросов провалился:", err);
});

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
  1. Ранний выход (Promise.resolve/reject):

Код ITЗагрузка примера кода…

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Промисы используют микрозадачи (microtask queue) – они выполняются сразу после текущего синхронного кода, но перед макрозадачами (setTimeout, UI-рендеринг).

console.log("Старт");

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

Promise.resolve()
.then(() => console.log("Промис 1"))
.then(() => console.log("Промис 2"));

console.log("Конец");

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Промис – контейнер для будущего результата.

Promise.all запускает операции параллельно.

Возврат промиса из функции:

function <имя функции>(<параметры>) {
return new Promise((resolve, reject) => {
// асинхронная логика
});
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Преобразование значения в разрешённый промис:

<промис> = Promise.resolve(<значение>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Создание отклонённого промиса:

<промис> = Promise.reject(<причина ошибки>);

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Async / Await

async/await даёт синтаксис промисов в линейном стиле.

  • async помечает функцию, которая всегда возвращает промис;
  • await ставит паузу внутри этой функции, пока промис не завершится;
  • основной поток JavaScript остаётся свободным.

Базовая форма:

async function loadUser() {
const response = await fetch('/api/user');
return response.json();
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Когда использовать await по очереди

Последовательный сценарий подходит, когда второй шаг зависит от результата первого.

async function loadUserAndPosts() {
const user = await fetch('/api/user').then(r => r.json());
const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
return { user, posts };
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Когда запускать параллельно

Если зависимости между задачами нет, запускайте их вместе через Promise.all.

async function loadDashboard() {
const [profile, settings, notifications] = await Promise.all([
fetch('/api/profile').then(r => r.json()),
fetch('/api/settings').then(r => r.json()),
fetch('/api/notifications').then(r => r.json())
]);

return { profile, settings, notifications };
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Ошибки в async/await

try/catch перехватывает отклонённый промис и ошибки, брошенные в блоке try.

async function loadWithHandling() {
try {
const response = await fetch('/api/user');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Ошибка загрузки:', error.message);
return null;
}
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Типичные ошибки и как исправлять

  • await вне async-функции — оборачивайте код в async function (или используйте top-level await в ESM-модуле);
  • лишняя последовательность await в цикле — собирайте промисы и ждите их через Promise.all;
  • пропущенный await — функция возвращает промис вместо готовых данных;
  • пустой catch — ошибка теряется, логируйте или пробрасывайте дальше.

AJAX

AJAX (Asynchronous JavaScript and XML) – технология для загрузки данных без перезагрузки страницы. Браузер отправляет HTTP-запрос в фоне, сервер возвращает данные (в JSON/XML), JS динамически обновляет страницу.

Классический AJAX-алгоритм:

  • создать запрос (Fetch API или XMLHttpRequest);
  • отправить запрос с параметрами (метод, заголовки, тело);
  • обработать ответ (JSON, текст, бинарные данные);
  • обновить UI или обработать ошибку.
async function fetchData(url, method = 'GET', body = null) {
const options = { method };
if (body) options.body = JSON.stringify(body);

const response = await fetch(url, options);
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);

return response.json(); // Автоматический парсинг JSON
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Event Loop – цикл событий, состоящий из фаз:

  • выполнить синхронный код до конца;
  • обработать микрозадачи (промисы, queueMicrotask);
  • обработать макрозадачи (setTimeout, setInterval, UI-рендеринг).
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('End');

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с асинхронным выполнением.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


Практические рекомендации по async

Когда код становится большим, главное — не только "чтобы работало", но и чтобы ошибки не терялись.

Минимальный продакшн-шаблон для запроса:

async function getJson(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}

Разбор:

  • Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
  • Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
  • Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
  • Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
  • В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
  • Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
  • Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
  • Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.

Рекомендации:

  • всегда обрабатывать ошибки (try/catch или .catch);
  • в независимых задачах использовать Promise.all;
  • не запускать долгие синхронные циклы внутри обработчиков событий;
  • для отмены запросов использовать AbortController.

Практикум — последовательное и параллельное выполнение

Сценарий из практикума раздела "Асинхронность". В JavaScript один поток выполняет синхронный код; "параллельность" I/O — это пока ждём fetch, event loop обрабатывает другие задачи.

Последовательные await в цикле

Каждый await fetch останавливает async-функцию до ответа сервера. Второй запрос стартует после первого — задержки складываются.

async function sequential(urls) {
const start = performance.now();
for (const url of urls) {
await fetch(url);
}
console.log(((performance.now() - start) / 1000).toFixed(2), 'с');
}

Параллельный старт — Promise.all

Promise.all запускает все промисы сразу и ждёт завершения всех. Время близко к самому долгому запросу.

async function parallel(urls) {
const start = performance.now();
await Promise.all(urls.map((url) => fetch(url)));
console.log(((performance.now() - start) / 1000).toFixed(2), 'с');
}

Интерактив — Promise и async/await.

CPU в Node.js — worker_threads

Долгий цикл в главном потоке блокирует event loop. Модуль worker_threads выносит вычисления в отдельный поток V8; обмен — через postMessage или SharedArrayBuffer.

import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';

if (isMainThread) {
new Worker(new URL(import.meta.url), { workerData: { n: 1_000_000 } });
} else {
let sum = 0;
for (let i = 0; i < workerData.n; i++) sum += i;
parentPort.postMessage(sum);
}

В браузере аналог — Web Workers.

Полный листинг:

Код ITЗагрузка примера кода…

Сравнение с Python, Java, C# — практикум. Fetch API.


Читать дальше