Асинхронное программирование в 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. Она состоит из нескольких ключевых компонентов:
- Стек вызовов (Call Stack) - это стек, в котором хранятся функции, которые сейчас выполняются. JS работает по принципу LIFO (Last In, First Out) — последняя добавленная функция выполняется первой.
function greet() {
console.log("Привет!");
}
greet(); // → добавляется в стек, выполняется, удаляется
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на пошаговом выполнении кода.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Когда greet() вызывается, она попадает в стек. После завершения — удаляется.
- Web APIs (или Host APIs) - это внешние среды, предоставляемые браузером или Node.js, такие как:
- setTimeout, setInterval
- fetch, XMLHttpRequest
- addEventListener
- requestAnimationFrame
- Работа с DOM, файлами и т.д.
Когда вы вызываете setTimeout, JS не выполняет таймер сам. Он передаёт задачу в Web API, которое начинает отсчёт времени в фоне, независимо от JS. Поэтому и так получается - 1, 3, 2.
- Очереди: Task Queue (Macrotasks) и Microtask Queue. То есть, бывает два вида очередей - микрозадачи и макрозадачи. Когда асинхронная операция завершается, её коллбэк не выполняется сразу. Он помещается в одну из двух очередей:
Микрозадачи (очередь микрозадач) - высокий приоритет, и сюда попадают .then, .catch, .finally от промисов (Promise), queueMicrotask(), MutationObserver (отслеживание изменений DOM). Все микрозадачи выполняются до следующей макрозадачи.
Макрозадачи (очередь макрозадач) - низкий приоритет, и сюда попадают setTimeout, setInterval, setImmediate (в Node.js), события (click, input и т.д.), I/O операции. Одна задача из этой очереди выполняется за один цикл Event Loop.
- Event Loop (Цикл событий) - механизм, который постоянно проверяет:
- пуст ли стек вызовов?
- есть ли микрозадачи? Если да, то выполняет все из очереди микрозадач;
- есть ли макрозадачи? Если да, то выполняет одну из очереди макрозадач;
- возвращается к шагу (проверка стека вызовов).
Такой вот цикл - это работает по кругу снова и снова. Event Loop — это не поток и не таймер. Это просто цикл, который проверяет стек и очереди.
Микрозадачи и queueMicrotask
Микрозадача — короткая задача высокой приоритетности, которая выполняется сразу после завершения текущего синхронного участка кода и до перехода к следующей макрозадаче.
Ключевые источники микрозадач:
- обработчики
Promise.then,Promise.catch,Promise.finally; - продолжение
async-функции послеawait; queueMicrotask;MutationObserver.
Базовое правило цикла:
- выполняется текущий синхронный код;
- полностью очищается очередь микрозадач;
- берётся следующая макрозадача (
setTimeout, событие, I/O); - после неё снова очищается очередь микрозадач.
Из-за этого микрозадачи всегда выполняются раньше таймеров и других макрозадач.
Зачем нужен 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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Код выглядит простым, и давайте глянем, как всё работает:
console.log("1")— синхронный вызов → выполняется сразу → выводит 1;setTimeout(...)— передаёт коллбэк в Web API, которое начинает таймер (0 мс). Коллбэк ещё не в очереди, но будет помещён в очередь макрозадач, когда таймер сработает;Promise.resolve().then(...)— промис уже выполнен, .then помещает коллбэк в очередь микрозадач;console.log("4")— синхронный вызов → выполняется → выводит 4;- Стек вызовов пуст → Event Loop начинает работать;
- Проверка очереди микрозадач → есть одна задача
(console.log("3"))→ выполняется → выводит 3; - Очередь микрозадач пуста → Event Loop переходит к очереди макрозадач;
- Очередь макрозадач → есть setTimeout → выполняется → выводит 2.
Итого, в результате мы увидим 1, 4, 3, 2.
Важно — даже если setTimeout установлен на 0, он всегда ждёт, пока все микрозадачи будут выполнены. Промисы всегда приоритетнее таймеров.
Почему промисы в микрозадачах, а setTimeout — в макрозадачах? Потому что промисы — это часть логики программы, и их обработка должна быть немедленной и предсказуемой. Если вы используете .then(), вы ожидаете, что он выполнится как только промис разрешится, без задержек. В то время как setTimeout — это планировщик, и его задача — отложить выполнение на следующий "кадр" или цикл.
Пример:
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Что происходит:
- fetch — асинхронная операция → передаётся в Web API (браузер).
- JS не ждёт ответа → продолжает выполнять следующие строки.
- Когда сервер ответит, Web API помещает коллбэк из .then в очередь микрозадач.
- Как только стек освободится, Event Loop выполнит этот коллбэк.
- Таким образом, основной поток не блокируется, и страница остаётся отзывчивой.
А что такое асинхронная функция? Как можно понять, это будет как-то так:
async function load() {
console.log("A");
await fetch("/api"); // → ожидание
console.log("B");
}
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Здесь создаётся асинхронная функция load(), которая логирует A, потом выполняет fetch по адресу /api, ожидает и логирует B.
Функция становится асинхронной потому что:
- она помечена как async - всегда возвращает промис;
- внутри неё можно использовать await - логика приостанавливается на промисе;
- после 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.
Механизмы асинхронности
Механизмы асинхронности:
- Callback – функции, вызываемые после завершения операции.
- Promises (Промисы) – более удобная альтернатива колбэкам.
- 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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на пошаговом выполнении кода.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
В вышеприведённом примере это работает так, пошагово:
- loadData – это функция, которая ждёт callback (друг, ждущий инструкцию);
- Внутри loadData есть setTimeout – он имитирует долгую операцию (например, запрос к серверу).
- Через 1 секунду setTimeout вызывает callback("Данные загружены!").
- Мы передаём анонимную функцию (result)
=>{ console.log(result); }как callback. - Когда 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 не блокирует UI, куда попадают .then и почему console.log('B') выполняется раньше ответа сервера. То же демо — в живых примерах асинхронности.
Play ITЗагрузка интерактивного демо…
Pending → Fullfulled с результатом или Rejected с ошибкой.
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на пошаговом выполнении кода.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Promise (от английского) – "обещание" JavaScript сделать что-то асинхронное и сообщить результат — успех (fulfilled), ошибка (rejected), ожидание (pending) – ещё выполняется.

Итого, промисом является некий объект, представляющий будущий результат асинхронной операции, который может быть в одном из трёх состояний - ожидание, успех или ошибка. Давайте подготовим шаблон для создания промиса и разберём его:
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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Разбирая пример с пиццей, можно сделать это кодом в два этапа:
- Создание промиса:
const pizzaPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const isSuccess = Math.random() > 0.5; // 50% шанс успеха
if (isSuccess) {
resolve("Пицца доставлена!"); // Успех
} else {
reject("Пицца сгорела в печи!"); // Ошибка
}
}, 2000); // Через 2 секунды
});
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
- Использование промиса:
pizzaPromise
.then((result) => {
console.log(result); // "Пицца доставлена!"
})
.catch((error) => {
console.error(error); // "Пицца сгорела в печи!"
});
Разбор:
- Фрагмент показывает рабочий паттерн JavaScript и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
- new Promise создаёт объект с двумя функциями:
- resolve() – вызывается при успехе;
- reject() – вызывается при ошибке.
- Через 2 секунды "кухня" (setTimeout) сообщает результат.
.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 без перерисовки всей страницы — грубо, редко сегодня |
| 2 | XMLHttpRequest | Классический AJAX; колбэки, ручной разбор ответа |
| 3 | fetch | Promise, 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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с условной логикой, асинхронным выполнением, созданием объектов.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
Паттерны использования промисов
Паттерны использования промисов:
- Цепочка (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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
- Параллельное выполнение – запускаются несколько промисов одновременно и ждём все результаты. Если все промисы успешны -
.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 и его структуру от объявления до итогового действия.
- Сначала интерпретатор обрабатывает объявления переменных, функций и выражений, формируя контекст выполнения.
- Затем код исполняется сверху вниз: каждое выражение использует уже вычисленные значения и обновляет текущее состояние.
- Ключевые операторы и методы в примере управляют логикой ветвления, преобразованием данных и вызовами функций.
- В примере акцент сделан на работе с асинхронным выполнением.
- Порядок выполнения важен — сначала инициализация, потом основная обработка, затем получение и вывод результата.
- Итог работы блока — конкретный эффект: возвращаемое значение, изменение данных или вывод в консоль.
- Такой разбор помогает быстро перенести шаблон в реальный код и избежать ошибок в последовательности шагов.
- Ранний выход (
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.