PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • Подписка
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюсPurpleMax
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • Подписка
    • AI для кодаНовое
    • Сообщество
    Главная
    Сообщество
    Event Loop в Node.js: как работает и зачем это знать

    Event Loop в Node.js: как работает и зачем это знать

    Аватар автора Event Loop в Node.js: как работает и зачем это знать

    Антон Ларичев

    Иконка календаря04 мая 2026
    Node.jsJavaScriptEvent LoopАсинхронностьBackendmiddleИконка уровня middle
    Картинка поста Event Loop в Node.js: как работает и зачем это знать

    Введение

    Event Loop — сердце Node.js, механизм, который позволяет однопоточной среде выполнения обрабатывать тысячи одновременных операций ввода-вывода. Понимание его работы критично для любого backend-разработчика: без этого знания легко написать код, который блокирует сервер, теряет производительность или ведёт себя непредсказуемо под нагрузкой.

    Node.js построен поверх движка V8 и библиотеки libuv. Именно libuv реализует Event Loop, делегируя тяжёлые операции (файловая система, сеть, DNS) пулу потоков и системным вызовам, а результаты возвращает в основной поток через очереди колбэков.

    Архитектура: однопоточность и неблокирующий I/O

    JavaScript в Node.js выполняется в одном потоке. Это значит, что в любой момент времени работает ровно одна функция. Чтобы при этом не простаивать на медленных операциях, Node перекладывает их на ядро ОС или на отдельные потоки libuv, а основной поток продолжает обрабатывать другие задачи.

    const fs = require('fs');
    
    // Неблокирующее чтение: основной поток свободен
    fs.readFile('/etc/hosts', (err, data) => {
      console.log('Файл прочитан');
    });
    
    console.log('Запрос отправлен');
    // Сначала выведется 'Запрос отправлен', потом 'Файл прочитан'
    

    Фазы Event Loop

    Event Loop проходит фазы по кругу. На каждой итерации (tick) выполняются колбэки из соответствующей очереди.

    1. Timers

    Выполняются колбэки setTimeout и setInterval, у которых истёк таймер. Важно: указанное время — это минимальная задержка, а не точная.

    2. Pending callbacks

    Колбэки некоторых системных операций — например, ошибки TCP-соединений.

    3. Idle, prepare

    Внутренние нужды libuv, разработчику недоступны.

    4. Poll

    Центральная фаза: получение новых событий ввода-вывода и выполнение их колбэков. Если очередь пуста, цикл может ждать здесь новых событий.

    5. Check

    Выполняются колбэки setImmediate. Они гарантированно сработают сразу после фазы poll.

    6. Close callbacks

    Колбэки закрытия — например, socket.on('close', ...).

    setTimeout(() => console.log('timeout'), 0);
    setImmediate(() => console.log('immediate'));
    
    // Порядок не гарантирован вне I/O контекста,
    // но внутри I/O колбэка setImmediate всегда раньше setTimeout
    

    Микрозадачи: process.nextTick и Promise

    Помимо фаз Event Loop существуют две очереди микрозадач, которые обрабатываются между любыми операциями:

    • process.nextTick — наивысший приоритет, выполняется до всего остального.
    • Promise jobs (.then, await) — следом за nextTick.
    setTimeout(() => console.log('timeout'), 0);
    setImmediate(() => console.log('immediate'));
    Promise.resolve().then(() => console.log('promise'));
    process.nextTick(() => console.log('nextTick'));
    
    console.log('sync');
    
    // Вывод:
    // sync
    // nextTick
    // promise
    // timeout (или immediate)
    // immediate (или timeout)
    

    Микрозадачи опустошаются полностью между фазами. Это удобно, но опасно: рекурсивный process.nextTick может полностью заблокировать Event Loop, не давая обработать ни одно I/O событие.

    Thread Pool и тяжёлые операции

    libuv держит пул потоков (по умолчанию 4, регулируется через UV_THREADPOOL_SIZE). В нём выполняются операции, которые ОС не умеет делать асинхронно: fs.*, crypto.pbkdf2, zlib, DNS через dns.lookup.

    const crypto = require('crypto');
    
    const start = Date.now();
    for (let i = 0; i < 4; i++) {
      crypto.pbkdf2('pass', 'salt', 100000, 64, 'sha512', () => {
        console.log(`Задача ${i}: ${Date.now() - start}ms`);
      });
    }
    // Все 4 задачи завершатся примерно одновременно — пул вмещает 4 потока
    // Пятая задача будет ждать освобождения потока
    

    Частые ошибки

    Блокирующий код в основном потоке

    Тяжёлые синхронные вычисления (большие циклы, JSON.parse мегабайтных строк, синхронные fs.readFileSync) полностью останавливают Event Loop. Сервер перестаёт отвечать на запросы.

    // Плохо: блокирует поток на сотни миллисекунд
    app.get('/hash', (req, res) => {
      const hash = crypto.pbkdf2Sync('pass', 'salt', 1000000, 64, 'sha512');
      res.send(hash.toString('hex'));
    });
    
    // Хорошо: асинхронная версия отдаёт работу пулу потоков
    app.get('/hash', (req, res) => {
      crypto.pbkdf2('pass', 'salt', 1000000, 64, 'sha512', (err, hash) => {
        res.send(hash.toString('hex'));
      });
    });
    

    Голодание I/O через nextTick

    Рекурсивный process.nextTick не даёт циклу выйти за пределы текущей фазы.

    // Опасно: I/O колбэки никогда не выполнятся
    function loop() {
      process.nextTick(loop);
    }
    loop();
    

    Путаница setTimeout(fn, 0) и setImmediate

    Вне I/O контекста порядок не детерминирован. Если нужен гарантированный порядок «после текущего I/O» — используйте setImmediate.

    Игнорирование размера thread pool

    Если приложение активно использует crypto или fs, дефолтных 4 потоков мало. Увеличьте через переменную окружения:

    UV_THREADPOOL_SIZE=16 node app.js
    

    Заключение

    Event Loop — не магия, а строгая последовательность фаз с предсказуемыми правилами. Зная порядок Timers → Poll → Check, приоритет микрозадач и ограничения thread pool, вы сможете писать Node.js-приложения, которые держат нагрузку и не падают под пиками. Главное правило: никогда не блокируйте основной поток — выносите тяжёлые вычисления в worker threads, отдельные сервисы или асинхронные API.

    Иконка глаза1 661

    Комментарии

    0

    Постройте личный план изучения TypeScript с нуля - полный курс и паттерны проектирования до уровня Middle — бесплатно!

    TypeScript с нуля - полный курс и паттерны проектирования — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    React и Redux Toolkit

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Zustand

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Neovim

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Профессиональное выгорание программиста: признаки и как с ним бороться
    Иконка аватараАнтон
    Иконка календаря28 октября 2026
    выгораниепродуктивностьsoft skills+ 2middleИконка уровня middle

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

    Выгорание программиста — не абстракция, а измеримое явление: падение скорости коммитов, рост багов, ночные дедлайны. Разбираем признаки, причины и рабочие способы восстановиться.

    Иконка чипа0
    Иконка глаза184
    Иконка комментариев0
    Картинка поста Neovim + avante.nvim с Claude из России: настройка
    Иконка аватараАнтон
    Иконка календаря27 октября 2026
    AI-инструментыAnthropicразработкаmiddleИконка уровня middle

    Neovim + avante.nvim с Claude из России: настройка

    avante.nvim — плагин для Neovim, который добавляет ИИ-ассистента в стиле Cursor: боковую панель с чатом по коду, правки выделенного фрагмента и применение изменений прямо в буфер. С Claude он работает через провайдер `claude`, но из России напрямую не подключится: API Anthropic отклоняет российские адреса, а ключ оплачивается зарубежной картой. Решение — указать в конфиге плагина свой адрес API (`endpoint`) и ключ AI для кода от PurpleSchool с оплатой в рублях. Ниже — установка через lazy.nvim, актуальный конфиг, горячие клавиши и частые ошибки.

    Иконка чипа0
    Иконка глаза238
    Иконка комментариев0
    Картинка поста OpenCode: пользовательские провайдеры недоступны — что делать
    Иконка аватараАнтон
    Иконка календаря26 октября 2026
    AI-инструментыapiразработкаjuniorИконка уровня junior

    OpenCode: пользовательские провайдеры недоступны — что делать

    Ошибка «Пользовательские провайдеры недоступны на этом сервере» (в английском интерфейсе — «Custom providers are unavailable on this server») появляется в OpenCode Desktop и веб-интерфейсе OpenCode, когда вы сохраняете своего провайдера через «Настройки → Провайдеры → Пользовательский провайдер». Это ошибка самого OpenCode, а не вашего ключа, сервера или региона: в версиях 2.0.x форма сохранения не подключена к серверу и выдаёт эту ошибку при любых введённых данных. VPN и смена ключа не помогут. Решение — описать провайдера в файле `opencode.json`: так он работает и в Desktop, и в терминальной версии. Ниже — что именно сломано, готовые конфиги для Claude, GPT и Qwen и что делать, если модели не появились.

    Иконка чипа0
    Иконка глаза1 265
    Иконка комментариев0
    Иконка чипа0