Перейти к содержанию

Асинхронный JavaScript: колбэки, промисы, async/await

Начало урока

Асинхронный JavaScript

Колбэки, промисы и async/await в Node.js

Главная идея: Node.js выполняет код в одном главном потоке, поэтому он не простаивает, ожидая медленных задач вроде чтения файлов или сетевых запросов. Вместо этого он запускает медленную задачу, продолжает работу и возвращается к результату позже. Этот стиль «вернуться позже» называется асинхронным программированием.
К концу урока ты сможешь:
  • объяснить цикл событий и почему Node не блокируется,
  • читать и писать код с колбэками (и распознавать «ад колбэков»),
  • использовать промисы с .then() / .catch(),
  • писать понятный асинхронный код с async / await.

1. Блокирующий и неблокирующий код

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

Node.js работает как умный официант: он ставит блюдо в духовку, заводит таймер и продолжает обслуживать другие столики. Когда таймер зазвонит, он приносит блюдо. Это неблокирующая работа. Посмотри на порядок сообщений ниже:

const fs = require('fs');

// Node does NOT wait here - it starts the read and moves on
fs.readFile('note.txt', 'utf8', function (err, data) {
  if (err) throw err;
  console.log('Inside callback:', data);
});

console.log('After readFile call');

// Output:
// After readFile call
// Inside callback: Hello from the file!

Строка After readFile call печатается первой, хотя написана последней, потому что Node не стал ждать файл. Когда файл загрузился, Node выполняет функцию-колбэк.

2. Цикл событий (event loop)

Как один поток делает много дел «одновременно»? Секрет — цикл событий. Твой синхронный код выполняется на стеке вызовов. Медленные задачи (таймеры, чтение файлов, сеть) передаются фоновым API Node. Когда задача завершается, её колбэк ждёт в очереди. Цикл событий постоянно проверяет: «стек пуст? тогда возьми следующий колбэк из очереди и выполни его».

Стек вызовов работающий код API Node таймеры, файлы, сеть Очередь колбэков (макрозадачи: таймеры) Очередь микрозадач (промисы) Цикл событий
Стек вызовов выполняет код; завершённые задачи ставят колбэки в очередь; цикл событий возвращает их на пустой стек. Микрозадачи (промисы) идут раньше следующей макрозадачи (таймеров).

Очередей две. Микрозадачи (разрешённые промисы) обрабатываются раньше следующей макрозадачи (например, setTimeout). Этот порядок объясняет классическую загадку:

console.log('1: start');

setTimeout(function () {
  console.log('2: timeout');
}, 0);

Promise.resolve().then(function () {
  console.log('3: promise');
});

console.log('4: end');

// Output:
// 1: start
// 4: end
// 3: promise
// 2: timeout

Сначала выполняются обе синхронные строки (1, 4). Затем колбэк промиса (3), потому что микрозадачи идут первыми. Колбэк таймера (2) — последним.

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя