К содержанию
Первый Коммит

Модуль 1

Введение в Node.js

  • Что такое Node.js
  • V8
  • Событийный цикл
  • Установка
  • nvm
  • npm
  • Первый скрипт
  • REPL

#1.1 Что такое Node.js

Node.js — это среда выполнения (runtime) JavaScript вне браузера. До появления Node.js в 2009 году JavaScript мог работать только внутри браузера: управлять DOM-деревом, обрабатывать клики и выполнять анимации. Райан Дал (Ryan Dahl) взял движок V8 из Google Chrome, добавил к нему API для работы с файловой системой, сетью и операционной системой — так появился Node.js.

Движок V8 компилирует JavaScript в машинный код, что обеспечивает высокую скорость выполнения. Node.js — не язык программирования и не фреймворк, а среда, в которой выполняется JavaScript-код на сервере.

Ключевые особенности Node.js:

  • Однопоточность с событийным циклом (Event Loop) — вместо создания нового потока на каждый запрос Node.js использует один поток и очередь событий, что экономит память
  • Неблокирующий ввод/вывод (Non-blocking I/O) — операции чтения файлов, запросы к БД и сети не блокируют основной поток, а выполняются в фоне
  • Высокая производительность для I/O-задач — Node.js хорошо подходит для приложений с большим количеством одновременных подключений (чаты, API, стриминг)
  • Огромная экосистема — npm (Node Package Manager) содержит более 2 миллионов пакетов
  • Один язык для фронтенда и бэкенда — команда может использовать JavaScript везде, что упрощает разработку

Где используется Node.js на практике:

  • REST API и GraphQL-серверы — основное применение, обработка HTTP-запросов
  • Реалтайм-приложения — чаты, онлайн-игры, совместное редактирование (WebSocket)
  • Микросервисы — лёгкие сервисы, которые быстро стартуют и потребляют мало памяти
  • CLI-утилиты — webpack, vite, eslint, prettier написаны на Node.js
  • SSR (Server-Side Rendering) — Next.js, Nuxt.js рендерят страницы на сервере
  • Десктоп-приложения — VS Code, Slack, Discord построены на Electron (Node.js + Chromium)

Важно понимать, для чего Node.js не подходит: для тяжёлых вычислений (обработка видео, машинное обучение, сложные математические расчёты). Поскольку Node.js однопоточный, длительные CPU-операции блокируют весь сервер. Для таких задач лучше использовать Python, Go или Rust.

#1.2 Установка Node.js

Существует несколько способов установки Node.js. Рекомендуется использовать менеджер версий nvm, потому что он позволяет легко переключаться между версиями Node.js. Это важно, когда разные проекты требуют разных версий.

#Установка через nvm (рекомендуемый способ)

nvm (Node Version Manager) — утилита для управления версиями Node.js. Она позволяет установить несколько версий одновременно и переключаться между ними одной командой.

Установка nvm на macOS / Linux:

Терминал
# Установка nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# Перезапустите терминал, затем:
nvm install --lts          # Установить LTS-версию (рекомендуется)
nvm install 24             # Установить конкретную версию
nvm use 24                 # Переключиться на версию 24
nvm ls                     # Список установленных версий
nvm alias default 24       # Установить версию по умолчанию

LTS (Long Term Support) — версия с долгосрочной поддержкой. Она получает исправления безопасности в течение 30 месяцев (12 — активная поддержка, 18 — сопровождение). Для продакшена всегда используйте LTS.

Установка на Windows:

На Windows используется отдельный проект nvm-windows. Откройте страницу github.com/coreybutler/nvm-windows, скачайте файл nvm-setup.exe из последнего релиза и запустите его — откроется мастер установки, пройдите его шаги со значениями по умолчанию (может потребоваться подтверждение прав администратора). После завершения мастера обязательно откройте новый терминал (cmd или PowerShell) — в ранее открытых окнах команда nvm недоступна. Затем выполните:

Терминал
nvm install lts
nvm use lts

Если nvm не найден после установки (command not found: nvm), проверьте, что в ~/.zshrc или ~/.bashrc добавлены строки установщика. Если их нет, добавьте строки вручную:

Код
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

После этого перезапустите терминал — команда nvm станет доступна.

Проверка установки:

Терминал
node --version    # v24.x.x
npm --version     # 11.x.x

Если обе команды выводят номера версий — установка прошла успешно. Вместе с Node.js автоматически устанавливается npm — менеджер пакетов, который подробно рассматривается в модуле 2.

#1.3 Глобальный объект process

В браузере глобальный объект — window, в Node.js его аналог — global (раздел 1.7). Объект process тоже доступен везде без импорта: он предоставляет информацию о текущем процессе и позволяет взаимодействовать с операционной системой.

Основные свойства и методы process:

  • process.version — версия Node.js (например, "v24.4.0")
  • process.platform — операционная система ("darwin" для macOS, "linux", "win32")
  • process.cwd() — текущая рабочая директория (откуда запущен скрипт)
  • process.argv — массив аргументов командной строки
  • process.env — переменные окружения (например, process.env.HOME, process.env.PATH)
  • process.exit(code) — завершить процесс (0 = успех, 1 = ошибка)
  • process.memoryUsage() — использование памяти
  • process.uptime() — время работы процесса в секундах

Файлы с кодом удобно создавать в редакторе — например, в бесплатном Visual Studio Code (скачать можно с code.visualstudio.com). Сохраните файл в отдельную папку проекта. Важно: терминал, из которого запускается node app.js, должен находиться в той же папке, что и файл (текущую папку показывает команда pwd — на macOS, Linux и в PowerShell; в cmd.exe — cd без аргументов); иначе запускайте node с полным путём к файлу.

Создайте файл app.js:

Код
// app.js
console.log('Привет, Node.js!');
console.log('Версия Node:', process.version);
console.log('Платформа:', process.platform);
console.log('Текущая директория:', process.cwd());
console.log('Аргументы:', process.argv.slice(2));
console.log('Память:', process.memoryUsage().heapUsed, 'байт');

Обратите внимание на process.argv.slice(2): первые два элемента массива argv — это путь к Node.js и путь к скрипту. Пользовательские аргументы начинаются с индекса 2.

Для запуска скрипта выполните команду:

Терминал
node app.js

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

Терминал
node app.js --name Alex --port 3000

#1.4 REPL — интерактивная консоль

REPL (Read-Eval-Print Loop) — интерактивный режим Node.js. Node.js вычисляет введённое выражение и сразу выводит результат. Это удобно для быстрых экспериментов и отладки.

Чтобы войти в REPL, введите node без аргументов:

JavaScript
$ node
> 2 + 2
4
> 'Hello'.toUpperCase()
'HELLO'
> const arr = [1, 2, 3]
> arr.map(x => x * 2)
[ 2, 4, 6 ]
> Math.random()
0.7234...
> .help     // Список команд REPL
> .exit     // Выход из REPL

Полезные команды REPL (точка перед командой нужна):

  • .help — справка
  • .clear — очистка контекста
  • .editor — многострочный режим
  • .exit — выход

Также можно нажать Tab дважды для автодополнения.

#1.5 Запуск скриптов: --watch, --env-file, --test

Начиная с Node.js 22 (LTS) возможности, для которых раньше требовались сторонние утилиты (nodemon, dotenv, mocha), встроены в сам Node. В Node 24 LTS все они стабильны и рекомендованы к использованию в реальных проектах.

С этого раздела в примерах появляется синтаксис import (ES-модули). В Node 24 файл с import исполняется как ES-модуль автоматически: достаточно сохранить его с обычным расширением .js и запустить node file.js — Node сам определит режим по наличию import. Это работает, пока рядом нет package.json с полем "type": "commonjs" (его записывает npm init -y в npm 11): тогда import упадёт с SyntaxError, и значение поля нужно поменять на "module". При желании режим можно включить явно: расширением .mjs или строкой "type": "module" в package.json. Устройство CommonJS и ES-модулей, отличия require от import и назначение package.json подробно разобраны в модуле 2, а пока достаточно пользоваться import в примерах ниже.

#node --watch — автоперезапуск при изменении файлов

Раньше для разработки с автоматическим перезапуском сервера при изменении кода использовали nodemon — отдельный пакет, который требовалось устанавливать через npm. С Node 22 это встроено в сам Node:

Терминал
# Запустить app.js с автоматическим перезапуском
node --watch app.js

# Следить только за определённой папкой
node --watch-path=src app.js

# Сохранять вывод между перезапусками
node --watch --watch-preserve-output app.js

Опция --watch-preserve-output не очищает консоль перед каждым перезапуском — удобно, когда нужно видеть всю историю логов. По умолчанию консоль очищается.

Для демонстрации --watch соберём простейший веб-сервер на встроенном модуле node:http. Полноценную работу с HTTP и маршрутизацией мы разберём в модуле 4, а здесь достаточно минимального варианта. Создайте файл server.js со следующим содержимым:

JavaScript
// server.js
import { createServer } from 'node:http';

const PORT = process.env.PORT || 3000;

const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Привет от Node.js!');
});

server.listen(PORT, () => {
  console.log(`Сервер запущен на http://localhost:${PORT}`);
});

Запустите его командой node --watch server.js и откройте в браузере адрес http://localhost:3000 — отобразится текст ответа. Теперь измените строку в res.end(...) и сохраните файл: Node сам перезапустит сервер. Обновите страницу в браузере — ответ станет новым. Порт берётся из переменной окружения process.env.PORT с запасным значением 3000 — это пригодится в связке с --env-file ниже и в практике.

#node --env-file — загрузка .env без dotenv

До Node 20 для чтения переменных из .env-файла нужен был пакет dotenv. Начиная с Node 20 он не требуется. Сначала создайте файл .env со следующим содержимым:

Код
PORT=3000
DB_URL=postgres://localhost:5432/mydb

Затем запустите скрипт с автоподгрузкой переменных:

Терминал
node --env-file=.env app.js

Внутри app.js переменные доступны через process.env.PORT, process.env.DB_URL. Импортировать 'dotenv/config' при этом не требуется.

Можно указать несколько файлов — они применяются по очереди, последний переопределяет значения предыдущих:

Терминал
node --env-file=.env --env-file=.env.local app.js

#node --test — встроенный тест-раннер

В Node 18 появился встроенный модуль node:test для написания тестов без Jest/Mocha. В Node 20 он стал стабильным. Минимальный пример:

JavaScript
// math.test.js
import { test } from 'node:test';
import assert from 'node:assert';

test('сложение работает', () => {
  assert.strictEqual(2 + 2, 4);
});

test('массивы', () => {
  assert.deepStrictEqual([1, 2], [1, 2]);
});

Запустите тесты:

Терминал
node --test math.test.js          # один файл
node --test                       # все *.test.js в проекте
node --test --test-reporter=spec  # подробный вывод

Подробно тестирование (включая supertest для HTTP, моки, fixtures) разбирается в модуле 12.

#Полезные комбинации флагов

В реальной разработке часто используют комбинации флагов. Сохраняйте их в npm-скрипты package.json:

JSON
{
  "scripts": {
    "dev": "node --watch --env-file=.env src/app.js",
    "test": "node --test --test-reporter=spec",
    "test:watch": "node --test --watch"
  }
}

Запускайте через npm run dev — Node будет перезапускать сервер при изменении кода и автоматически загружать переменные из .env.

#1.6 Event Loop (событийный цикл)

Событийный цикл — основной механизм Node.js. Понимание Event Loop критически важно для написания эффективного серверного кода.

Традиционные серверы (Apache, PHP) создают отдельный поток для каждого входящего запроса. При обработке 10 000 одновременных подключений серверу требуется 10 000 потоков, каждый из которых потребляет память.

Node.js работает иначе: один поток обрабатывает все запросы. Когда приходит запрос, требующий чтения файла или обращения к базе данных, Node.js не ждёт завершения операции, а регистрирует callback-функцию и переходит к следующему запросу. Когда операция завершается, callback помещается в очередь, и Event Loop его выполняет.

Такой подход называется неблокирующим вводом/выводом (Non-blocking I/O). Благодаря этому один процесс Node.js может обрабатывать десятки тысяч одновременных подключений.

Event Loop обрабатывает задачи в определённом порядке. Рассмотрим пример. Создайте новый файл eventloop.js с содержимым:

JavaScript
console.log('1: Начало');

setTimeout(() => {
    console.log('2: Таймер (макрозадача)');
}, 0);

Promise.resolve().then(() => {
    console.log('3: Promise (микрозадача)');
});

console.log('4: Конец');

Запустите его, выполнив:

Терминал
node eventloop.js

Вывод скрипта будет следующим:

Код
1: Начало
4: Конец
3: Promise (микрозадача)
2: Таймер (макрозадача)

Разберём, почему вывод получается таким. Казалось бы, текст «Таймер» должен быть выведен сразу после текста «Начало», как в других скриптовых языках, например PHP.

В Node.js это устроено иначе. Порядок выполнения следующий:

  • Сначала выполняется весь синхронный код (сообщения 1 и 4)
  • Затем микрозадачи — Promise.then(), queueMicrotask(). У них наивысший приоритет
  • Затем макрозадачи — setTimeout, setInterval, операции ввода/вывода

Это значит, что даже setTimeout с задержкой 0 мс выполнится после всех промисов. Понимание этого порядка поможет избежать неожиданных ошибок в асинхронном коде.

#process.nextTick — выше микрозадач

Кроме setTimeout (макрозадача) и Promise.then (микрозадача), Node предоставляет ещё один способ отложить выполнение — process.nextTick. Колбэки nextTick выполняются сразу после текущей синхронной операции, до любых других микрозадач:

JavaScript
console.log('1: Начало');

Promise.resolve().then(() => console.log('2: Promise'));
process.nextTick(() => console.log('3: nextTick'));
setTimeout(() => console.log('4: Timeout'), 0);

console.log('5: Конец');

Порядок вывода будет: 1, 5, 3, 2, 4. То есть nextTick срабатывает раньше Promise. На практике nextTick используют редко — при рекурсивном вызове он «морит голодом» остальные микрозадачи. Знать о нём полезно, чтобы понимать чужой код (например, внутреннее устройство Express).

#queueMicrotask — стандартный способ добавить микрозадачу

queueMicrotask — это веб-API, доступный и в браузере, и в Node. Он добавляет задачу в ту же очередь, что и Promise.then:

JavaScript
queueMicrotask(() => {
  console.log('Это микрозадача');
});

Когда использовать: если нужно отложить выполнение на «конец текущего тика» без оборачивания в Promise. Например, чтобы DOM (в браузере) или внутренние структуры Node успели обновиться до продолжения работы кода.

#Фазы Event Loop (упрощённо)

Event Loop в Node работает в несколько фаз, каждая со своей очередью колбэков. В упрощённом виде:

  • Timers — выполняет колбэки setTimeout и setInterval, чьё время истекло
  • Pending callbacks — некоторые системные колбэки (например, ошибки сети, отложенные с предыдущего тика)
  • Poll — основная фаза: получает новые I/O-события (чтение файлов, сетевые ответы) и выполняет их колбэки
  • Check — выполняет setImmediate (это макрозадача с приоритетом сразу после Poll)
  • Close — выполняет закрывающие колбэки (socket.on('close') и т.п.)

После каждой фазы Node выполняет все накопившиеся микрозадачи (nextTick → Promise.then) и только потом переходит к следующей фазе. Поэтому setTimeout(fn, 0) часто срабатывает позже, чем можно ожидать — между двумя тиками успевают пройти десятки микрозадач.

#setImmediate vs setTimeout(fn, 0)

Обе функции выполняют код «как можно скорее, но не сейчас». Разница: setImmediate срабатывает в фазе Check (после Poll), setTimeout(fn, 0) — в фазе Timers (минимум через 1 мс из-за округления). Порядок их срабатывания не гарантирован — он зависит от того, сколько времени Node провёл в Poll. Внутри I/O-колбэка setImmediate всегда срабатывает раньше setTimeout:

JavaScript
import fs from 'node:fs';

fs.readFile('file.txt', () => {
  setTimeout(() => console.log('timeout'), 0);
  setImmediate(() => console.log('immediate'));
});
// Вывод: immediate, потом timeout

#1.7 Глобальные объекты Node.js

В Node.js без импорта доступны такие глобальные объекты (таймеры и console есть и в браузере, остальных там нет):

  • __dirname — абсолютный путь к директории текущего файла (только в CommonJS)
  • __filename — абсолютный путь к текущему файлу (только в CommonJS)
  • global — глобальный объект (аналог window в браузере)
  • Buffer — для работы с бинарными данными (изображения, файлы)
  • setTimeout, setInterval, setImmediate — таймеры
  • console — вывод в терминал (console.log, console.error, console.table, console.time)

В ES Modules (с "type": "module" в package.json или с расширением .mjs) __dirname и __filename недоступны. Вместо них используется объект import.meta:

  • import.meta.url — URL текущего файла (вида file:///path/to/file.js)
  • import.meta.dirname — абсолютный путь к директории (есть с Node 20.11, стабильно в Node 24)
  • import.meta.filename — абсолютный путь к файлу (есть с Node 20.11, стабильно в Node 24)
  • import.meta.resolve(specifier) — разрешает путь модуля относительно текущего файла

Пример сравнения CommonJS и ESM:

Код
// CommonJS (require/exports)
console.log(__dirname);  // /home/user/project/src
console.log(__filename); // /home/user/project/src/app.js

// ESM (import/export)
console.log(import.meta.dirname);  // тот же путь
console.log(import.meta.filename); // тот же путь

Функция console.table() особенно полезна для отладки — она выводит массивы и объекты в виде таблицы. Создайте новый файл table.js с содержимым:

JavaScript
const users = [
    { name: 'Алексей', age: 25 },
    { name: 'Мария', age: 30 },
];
console.table(users);

Затем выполните его, запустив:

Терминал
node table.js

Скрипт выведет таблицу.

Функции console.time() и console.timeEnd() позволяют измерять время выполнения. Создайте новый файл loop.js с содержимым:

JavaScript
console.time('loop');

let sum = 0;
for (let i = 0; i < 1_000_000; i++) {
  sum += i;
}

console.timeEnd('loop');

Выполните его, запустив:

Терминал
node loop.js

Скрипт выведет время выполнения в миллисекундах.

#1.8 Worker Threads — параллелизм для CPU-задач

Как отмечено в разделе 1.1, Node.js однопоточный и не подходит для тяжёлых вычислений — они блокируют Event Loop. Но такая задача может появиться в проекте: например, нужно сжать изображение или обработать большой массив данных в самом приложении. На этот случай в Node есть встроенный модуль worker_threads.

Worker Thread — это отдельный поток ОС с собственным V8-инстансом и своим Event Loop. Главный поток обменивается с воркером сообщениями (postMessage / on('message')). В отличие от обычных потоков в других языках, воркеры не делят память напрямую — это безопаснее (нет race conditions) и проще для понимания.

#Пример: фоновое вычисление

Допустим, нужно вычислить сумму чисел от 1 до 10 млрд. В главном потоке это заблокирует сервер секунд на десять (на Node 24 цикл идёт около 13 с). Сама сумма при этом выйдет за пределы точных целых чисел JavaScript (Number.MAX_SAFE_INTEGER), и последние цифры будут неточными; для примера это неважно — он про нагрузку, а точную сумму даёт формула n(n + 1)/2. Создадим воркер:

JavaScript
// worker.js
import { parentPort, workerData } from 'node:worker_threads';

const { limit } = workerData;
let sum = 0;
for (let i = 0; i <= limit; i++) sum += i;

parentPort.postMessage(sum);

И запустим его из главного потока:

JavaScript
// main.js
import { Worker } from 'node:worker_threads';

console.log('Сервер продолжает работать...');

const worker = new Worker('./worker.js', {
  workerData: { limit: 10_000_000_000 },
});

worker.on('message', (sum) => {
  console.log('Воркер вернул:', sum);
});

worker.on('error', (err) => console.error('Ошибка воркера:', err));

Запустите node main.js. Главный поток не блокируется: сервер продолжает обрабатывать запросы, пока воркер выполняет вычисления. По завершении работы воркера придёт сообщение, и Event Loop вызовет обработчик.

#Когда использовать Worker Threads

  • Тяжёлые CPU-вычисления (сжатие, шифрование, хеширование больших данных)
  • Обработка изображений и видео (Sharp, FFmpeg)
  • Машинное обучение (использование моделей через TensorFlow.js)
  • Парсинг больших JSON/CSV-файлов (50+ МБ)
  • Любая задача, которая занимает > 50 мс CPU-времени в одном потоке

#Когда НЕ использовать Worker Threads

  • I/O-операции (чтение файлов, HTTP-запросы, БД) — для них работает обычный Event Loop, воркер их не ускорит
  • Простые операции (< 10 мс) — накладные расходы на создание воркера превысят выигрыш
  • Новый воркер на каждый запрос — это дорого; используйте пул воркеров (библиотека piscina)

#1.9 Что нового в Node.js 24 LTS

Node 24 вышла в мае 2025, в октябре 2025 получила статус LTS и на момент написания курса (май 2026) была текущей Active LTS. По сравнению с Node 20, на которой был основан предыдущий выпуск этого курса, ключевые изменения:

#Native TypeScript — node app.ts без сборки

Главная возможность Node 24 — встроенное выполнение TypeScript-файлов. Раньше для запуска .ts нужно было либо собирать их через tsc в .js, либо устанавливать ts-node/tsx. Теперь достаточно создать файл app.ts:

Код
const greeting: string = 'Hello, Node 24';
console.log(greeting);

И запустить его напрямую, без сборки:

Терминал
node app.ts

Node делает type stripping — удаляет все TypeScript-аннотации (типы, интерфейсы, type-only импорты) перед выполнением. После этого файл обрабатывается как обычный JavaScript.

  • Не проверяет типы — это всё ещё работа tsc или IDE. Скрипт с ошибкой типов запустится и отработает как обычный JavaScript: sum('2', 3) вернёт строку '23' без всякой ошибки
  • Не читает tsconfig.json — поэтому paths, baseUrl, target и прочие настройки игнорируются
  • Не транспилирует новые возможности языка в старый JS — целевой уровень тот же, что и у самого Node 24
  • Не поддерживает декораторы (TC39 Stage 3, ещё не вошли в стандарт)
  • Не поддерживает enum и namespace с кодом: на них Node остановится с ошибкой ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX

#Permission Model — изоляция доступа к файловой системе

В Node 24 есть стабильная Permission Model, которая позволяет запустить скрипт с ограниченным доступом — например, запретить запись на диск, запуск дочерних процессов и воркеров. Сетевые соединения она в Node 24 не ограничивает. Это полезно для запуска недоверенного кода (плагины, сторонние скрипты):

Терминал
# Разрешить чтение отовсюду, запись при этом запрещена
node --permission --allow-fs-read=* untrusted.js

# Разрешить чтение только из /tmp, запись запрещена
node --permission --allow-fs-read=/tmp untrusted.js

# Разрешить чтение отовсюду и запись только в папку output
node --permission --allow-fs-read=* --allow-fs-write=./output untrusted.js

Флаги --allow-fs-read и --allow-fs-write задают, откуда скрипту можно читать и куда писать: значение * означает любой путь, а конкретный путь ограничивает доступ этой папкой. Всё, что не разрешено явно, блокируется — например, попытка записи без --allow-fs-write завершится ошибкой с кодом ERR_ACCESS_DENIED. На Windows вместо /tmp и ./output подставляйте свои пути (например, C:\Temp или относительную папку output).

С Node 22.13 эта функция стабильна и запускается без предупреждений. Идея та же, что в Deno: разрешения скрипта объявляются явно.

#Прочие улучшения

  • V8 13.6 — поддержка новых возможностей JS: Set methods (intersection, union, difference), Iterator Helpers (.map, .filter на итераторах)
  • Встроенный WebSocket-клиент — теперь можно открывать WebSocket-соединения из Node без сторонних библиотек (ws, undici). Серверную часть пока пишут на socket.io или ws — об этом в модуле 11
  • fs.glob — встроенный поиск файлов по маске (раньше нужна была библиотека glob). Подробнее в модуле 3
  • node --run — запускает npm-скрипты быстрее, чем npm run, без накладных расходов на запуск npm CLI

#1.10 Практика

#Задание 1.1 — первый скрипт, REPL и порядок событий

  • Установите Node.js через nvm, проверьте версию
  • Создайте скрипт info.js, который выводит: дату, платформу, версию Node, аргументы командной строки, использование памяти
  • Поэкспериментируйте с REPL: выполните операции с массивами, объектами, Math, Date
  • Напишите пример с setTimeout, setImmediate и Promise — предскажите порядок вывода, затем проверьте его
  • Используйте console.time/timeEnd для измерения времени цикла из 1 000 000 итераций

#Задание 1.2 — флаги Node 24

  • Создайте файл server.js с минимальным HTTP-сервером (createServer из node:http) — шаблон такого сервера есть в разделе 1.5. Запустите его командой node --watch server.js. Внесите изменение — убедитесь, что сервер перезапустился автоматически
  • Создайте .env с переменной PORT=4000. Шаблон server.js из раздела 1.5 уже читает порт из process.env.PORT (по умолчанию 3000). Запустите: node --watch --env-file=.env server.js. Поменяйте значение в .env и убедитесь, что сервер перезапустился сам и слушает новый порт: как сказано в разделе 1.5, --watch следит и за файлом из --env-file
  • Создайте файл server.test.js с одним тестом: process.env.PORT должен быть равен '4000' после загрузки .env. Запустите node --test --env-file=.env server.test.js
  • Создайте файл script.ts с типизированной функцией (например, sum: (a: number, b: number) => number) и запустите его через node script.ts. Убедитесь, что он работает без сборки. Затем добавьте ошибку типа (передайте строку вместо числа) и проверьте, будет ли она обнаружена при запуске

#Задание 1.3 — Worker Threads

  • Напишите функцию heavyComputation(n) — например, проверку простоты числа n перебором всех делителей от 2 до n − 1. Запустите её в главном потоке для n = 2147483647 (простое число 2^31 − 1): перебор займёт несколько секунд. Заметьте, что всё это время главный поток занят — таймеры и другие колбэки ждут, пока цикл не закончится
  • Перенесите ту же функцию в worker.js, вызывайте её через new Worker(). Параллельно с воркером запустите setInterval, который каждую секунду выводит 'alive'. Убедитесь, что Node продолжает выводить 'alive', пока воркер выполняет вычисления

#Задание 1.4 — Event Loop (продвинутое)

  • Напишите скрипт, который выведет числа 1..5 в правильном порядке, используя разные механизмы отложенного выполнения: синхронный console.log, queueMicrotask, process.nextTick и setTimeout(fn, 0) (некоторые из них примените по нескольку раз, чтобы получить все пять чисел). Перед запуском попробуйте предсказать порядок на бумаге — затем сравните его с реальным выводом
  • Создайте Promise, который резолвится через setTimeout(resolve, 100). Внутри .then(...) добавьте ещё один setTimeout(fn, 0) и ещё один Promise.then. Зарисуйте на бумаге, какие колбэки в какой фазе Event Loop сработают

#Итоги модуля 1

  • Node.js — среда выполнения JavaScript вне браузера на движке V8; это не язык и не фреймворк; среда эффективна в I/O-задачах (REST API, чаты, микросервисы, SSR) и не подходит для тяжёлых CPU-вычислений.
  • Однопоточность и неблокирующий ввод/вывод — один поток с очередью событий обслуживает тысячи подключений: долгие операции выполняются в фоне, а их колбэки вызывает Event Loop.
  • nvm (macOS/Linux) и nvm-windows — менеджеры версий Node: nvm install --lts, nvm use, nvm alias default; для продакшена всегда используйте LTS-версию, а на Windows удобная альтернатива — WSL2.
  • Объект process — интерфейс к операционной системе: process.version, process.platform, process.cwd(), process.env, process.exit(code); пользовательские аргументы командной строки начинаются с индекса 2 массива process.argv.
  • REPL (команда node без аргументов) — интерактивная консоль для быстрых экспериментов: команды .help, .editor, .clear, .exit и автодополнение по Tab.
  • Встроенные флаги заменили сторонние пакеты: node --watch вместо nodemon, node --env-file=.env вместо dotenv, node --test вместо Jest/Mocha; полезные комбинации сохраняют в npm-скрипты package.json.
  • Минимальный HTTP-сервер собирается на встроенном модуле node:http (createServer и server.listen), порт удобно получать из process.env.PORT; под node --watch такой сервер сам перезапускается при правках (подробно HTTP разбирается в модуле 4).
  • Порядок Event Loop: сначала весь синхронный код, затем микрозадачи (Promise.then, queueMicrotask), затем макрозадачи (setTimeout, setInterval, I/O); setTimeout с нулевой задержкой выполняется после всех промисов.
  • process.nextTick внутри колбэков срабатывает раньше остальных микрозадач (рекурсивные вызовы «морят голодом» очередь), а queueMicrotask — стандартный способ добавить микрозадачу и в Node, и в браузере.
  • Фазы Event Loop: Timers, Pending callbacks, Poll, Check (в ней выполняется setImmediate), Close; после каждой фазы выполняются все накопившиеся микрозадачи; внутри I/O-колбэка setImmediate всегда срабатывает раньше setTimeout(fn, 0).
  • В ES Modules нет __dirname и __filename — вместо них import.meta.dirname и import.meta.filename (есть с Node 20.11, стабильны в Node 24); для отладки полезны console.table и пара console.time / console.timeEnd.
  • Worker Threads (модуль node:worker_threads) выносят тяжёлые CPU-задачи в отдельный поток со своим V8 и Event Loop: главный поток не блокируется, обмен идёт через workerData, postMessage и событие message.
  • Воркеры не нужны для I/O и мелких операций — накладные расходы на создание превысят выигрыш; child_process порождает отдельный процесс ОС (полная изоляция), worker_threads — поток внутри процесса (легче): для вычислений используйте воркеры, для запуска внешних программ — child_process.
  • Node 24 LTS выполняет TypeScript напрямую (node app.ts) через type stripping: типы удаляются перед запуском, но проверки типов нет, tsconfig.json не читается, декораторы и enum не поддерживаются — для production проверку типов делает tsc.
  • Прочие новинки Node 24 по сравнению с Node 20: стабильная Permission Model (--permission, --allow-fs-read), встроенный WebSocket-клиент, fs.glob и ускоренный запуск скриптов node --run.

#Домашнее задание

Задания выполняйте по порядку — каждое опирается на разделы модуля, и сложность растёт от задания к заданию.

  • Установите через nvm вторую версию Node (например, предыдущую LTS), переключитесь на неё командой nvm use и проверьте вывод node --version, затем вернитесь на Node 24 и назначьте её версией по умолчанию через nvm alias default.
  • Напишите скрипт greet.js, который берёт имя пользователя из process.argv, а язык приветствия — из переменной окружения GREETING_LANG; подготовьте файл .env и запускайте скрипт через node --env-file=.env greet.js, проверяя оба языка.
  • Создайте бенчмарк bench.js: сравните время сборки строки из 100 000 фрагментов через конкатенацию в цикле и через Array.join, замеры выполните парой console.time / console.timeEnd, а итоговое сравнение выведите через console.table.
  • Напишите файл strings.test.js с тремя тестами на node:test и node:assert (например, для функций capitalize, reverse и isPalindrome) и запустите их командой node --test --test-reporter=spec.
  • Перенесите в worker.js рекурсивное вычисление числа Фибоначчи; запустите два воркера параллельно с разными workerData (например, 40 и 42), соберите оба результата через событие message и убедитесь по console.time, что параллельный запуск быстрее последовательного.
  • (со звёздочкой, продвинутое) Испытайте Permission Model. Чтение и запись файлов через fs.readFile / fs.writeFile подробно разбираются в модуле 3 — если синтаксис fs пока незнаком, вернитесь к этому заданию после него. Напишите скрипт, который читает один файл и пытается записать другой, запустите его через node --permission --allow-fs-read=* и разберите ошибку записи, затем добавьте флаг --allow-fs-write (см. раздел 1.9) для одной папки и добейтесь успешного выполнения.

Войдите, чтобы отмечать пройденные модули и видеть прогресс по курсу.

Введите запрос: название модуля, тема или раздел.

Стрелки — выбор, Enter — открыть. Ищет по названиям модулей, темам и разделам.