Skip to content

Вопросы на собеседовании: Node.js-разработчик

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior Node.js-разработчик.

Смотреть пример резюме: Node.js-разработчик

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

Node.js представляет собой среду выполнения JavaScript вне браузера.

  • Он использует движок V8 для компиляции и выполнения JavaScript.
  • Он предоставляет серверные API для файлов, сети, процессов и взаимодействия с операционной системой.
  • Его событийная модель с неблокирующим вводом-выводом подходит сервисам с множеством одновременных I/O-операций.

Зачем это спрашивают: Интервьюер проверяет, понимаете ли вы Node.js как среду выполнения, а не язык или фреймворк.

V8 выполняет JavaScript, а libuv предоставляет event loop и кроссплатформенную поддержку асинхронного ввода-вывода.

  • V8 разбирает, компилирует, оптимизирует и выполняет JavaScript-код.
  • libuv координирует таймеры, готовность сетевых операций и работу, переданную в пул потоков.
  • Node.js связывает эти компоненты с JavaScript API, такими как fs, timers и сетевые модули.

Зачем это спрашивают: Сильный ответ разделяет выполнение JavaScript и инфраструктуру ввода-вывода с event loop.

concurrencyjavascript

Это означает, что один поток event loop обычно выполняет JavaScript callbacks процесса по одному.

  • Одновременные I/O-операции всё равно могут продвигаться через операционную систему и libuv.
  • Часть файловых, DNS, crypto и compression операций использует thread pool libuv.
  • Worker threads или child processes при необходимости дают дополнительные потоки или процессы выполнения JavaScript.

Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы однопоточное выполнение JavaScript от общей конкурентности runtime.

event-loop

Event loop это механизм, который последовательно выполняет готовые callbacks и координирует асинхронную работу без блокировки на каждой операции.

  • JavaScript запускает работу вроде таймера или сетевого запроса и регистрирует продолжение после её готовности.
  • Node.js обрабатывает очереди callbacks в определённых фазах.
  • Долгий синхронный JavaScript задерживает остальные callbacks, потому что поток event loop не может выполнять их одновременно.

Зачем это спрашивают: Сильный ответ связывает асинхронные операции, очереди callbacks и цену блокировки основного потока.

event-loop

Основные фазы это timers, pending callbacks, внутренние idle и prepare, poll, check и close callbacks.

  • Фаза timers запускает готовые callbacks setTimeout и setInterval.
  • Poll получает I/O-события и запускает многие I/O callbacks, а check запускает callbacks setImmediate.
  • На фазе close callbacks обрабатываются события закрытия, например закрытие сокета, а microtasks выполняются вокруг callbacks и не являются обычной фазой.

Зачем это спрашивают: Интервьюер оценивает, понимаете ли вы event loop как набор фаз с очередями, а не одну общую очередь callbacks.

Фаза timers делает callback доступным после истечения задержки, но не гарантирует точное время выполнения.

  • Задержка является минимальным порогом, а не назначенным моментом по часам.
  • Предыдущая синхронная работа и другие callbacks в очередях могут отложить запуск.
  • setInterval повторяет готовность через интервал, но его callbacks также задерживаются при нагрузке на event loop.

Зачем это спрашивают: Сильный ответ не считает JavaScript-таймеры точным планированием реального времени.

event-loop

Фаза poll получает новые I/O-события и запускает готовые callbacks для многих завершённых I/O-операций.

  • Она может ждать I/O, если callbacks пока не готовы и правила планирования не требуют перейти дальше.
  • На время ожидания влияют таймеры и callbacks setImmediate в очереди.
  • Не каждое асинхронное API завершается в poll, поэтому остальные фазы event loop тоже важны.

Зачем это спрашивают: Интервьюер проверяет понимание poll как основной I/O-фазы без ошибочного отнесения к ней всех callbacks.

setImmediate планирует callback для фазы check, а setTimeout для фазы timers после минимальной задержки.

  • Их относительный порядок в коде верхнего уровня нельзя считать общей гарантией.
  • Если оба вызова запланированы из одного I/O callback, setImmediate выполняется в ближайшей фазе check раньше таймера с нулевой задержкой.
  • Выбирайте API по нужной фазе, а не считайте любой из них командой выполнить немедленно.

Зачем это спрашивают: Сильный ответ называет фазы и не даёт чрезмерно упрощённого обещания порядка.

concurrency

process.nextTick ставит callback на выполнение после текущей JavaScript-операции и до продолжения event loop в другой фазе.

  • После завершения обычного callback Node.js очищает очередь nextTick перед Promise microtasks.
  • Выполнение ESM и уже начатая обработка microtasks не позволяют считать это универсальной гарантией порядка исходного кода.
  • Рекурсивное планирование nextTick может задержать таймеры и I/O, поэтому оно не заменяет обычное асинхронное планирование.

Зачем это спрашивают: Интервьюер оценивает, знаете ли вы приоритет nextTick и риск задержки остальных задач цикла событий.

promises

Promise reactions выполняются из очереди microtasks после текущего callback или script и до продолжения другой работы event loop.

  • then, catch, finally и продолжение после await планируются как microtasks.
  • Microtasks не являются отдельной фазой event loop libuv.
  • Длинная цепочка microtasks может задержать таймеры и I/O callbacks, хотя каждый шаг асинхронный.

Зачем это спрашивают: Сильный ответ размещает Promise callbacks между JavaScript-задачами и не называет их фазой event loop.

event-loop

Блокировка event loop не позволяет процессу выполнять другие JavaScript callbacks во время этой работы.

  • CPU-heavy циклы и синхронный I/O увеличивают задержку несвязанных запросов в том же процессе.
  • Неблокирующие API позволяют event loop обслуживать другую работу во время ожидания I/O.
  • Крупные CPU-задачи лучше переносить в worker threads, child processes или другой сервис.

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

concurrency

Пул потоков libuv выполняет отдельные блокирующие или CPU-intensive нативные операции вне потока event loop.

  • Частые примеры включают многие fs API, dns.lookup, crypto-операции и zlib.
  • Завершение передаётся обратно, чтобы соответствующий callback или Promise продолжился в потоке event loop.
  • I/O сетевых sockets обычно использует готовность операционной системы, а не отдельный поток pool на соединение.

Зачем это спрашивают: Сильный ответ называет характерную работу thread pool и не утверждает, что все асинхронные операции используют потоки.

asynccallbacks

Callback это функция, переданная другой операции для вызова после завершения работы или наступления события.

  • Запускающая функция может вернуться до выполнения асинхронного callback.
  • Callback получает результат, ошибку или аргументы события согласно контракту API.
  • Callbacks применяются и для синхронной итерации, поэтому асинхронность определяется контрактом API.

Зачем это спрашивают: Интервьюер проверяет, определяете ли вы callback через управление потоком без предположения, что любой callback асинхронный.

callbacks

Error-first callback получает ошибку первым аргументом, а данные успеха последующими аргументами.

  • null или отсутствие первого аргумента означает успех операции.
  • Если первый аргумент не равен null или undefined, ошибку обычно нужно обработать или передать дальше.
  • Многие старые Node.js API используют форму callback(error, result), которую можно адаптировать к Promise через promisify.

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

callbacks

Callback hell это глубоко вложенный асинхронный поток управления, в котором трудно отслеживать порядок, ошибки и очистку.

  • Именованные функции и ранние возвраты делают callback-код более плоским.
  • Promise выражают последовательности через возвращаемые цепочки и общую обработку catch.
  • async и await дают синтаксис, похожий на последовательный, сохраняя асинхронность Promise.

Зачем это спрашивают: Интервьюер проверяет понимание проблемы поддерживаемости, а не только узнавание вложенных отступов.

promises

Promise находится в состоянии ожидания pending, пока не завершится успешно fulfilled или с отклонением rejected.

  • Fulfilled и rejected являются двумя завершёнными settled-состояниями.
  • После завершения Promise не может изменить состояние или результат.
  • Обработчики можно добавить до или после завершения, и они всё равно выполнятся асинхронно как microtasks.

Зачем это спрашивают: Интервьюер оценивает, знаете ли вы жизненный цикл Promise и асинхронное поведение обработчиков.

promises

then обрабатывает fulfillment, catch обрабатывает rejection, а finally запускает код очистки после любого исхода.

  • Каждый метод возвращает новый Promise, позволяя продолжить композицию.
  • Возвращённое обработчиком значение переводит следующий Promise в fulfilled, а выброшенная ошибка отклоняет его.
  • finally обычно сохраняет предыдущее значение или ошибку, если его callback не выбросит ошибку и не вернёт rejected Promise.

Зачем это спрашивают: Сильный ответ объясняет семантику цепочки, а не описывает методы как простые callbacks.

promises

Возврат из обработчика then связывает его результат со следующим Promise в цепочке.

  • Возврат обычного значения выполняет следующий шаг успешно с этим значением.
  • Возврат Promise заставляет цепочку дождаться и принять его исход.
  • Забытый return может продолжить внешнюю цепочку с undefined раньше завершения вложенной работы.

Зачем это спрашивают: Интервьюер проверяет, понимаете ли вы сохранение порядка и ошибок через композицию Promise.

promises

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

  • Promise.all сохраняет порядок входов в массиве успешных результатов.
  • Его раннее отклонение не отменяет автоматически остальные запущенные операции.
  • allSettled возвращает объекты status для fulfilled и rejected входов, что полезно, когда важен каждый результат.

Зачем это спрашивают: Сильный ответ различает сбор результатов, поведение ошибок и отсутствие автоматической отмены.

async

Async-функция всегда возвращает Promise своего будущего результата.

  • Возврат обычного значения успешно выполняет этот Promise со значением.
  • Выброшенная внутри функции ошибка отклоняет возвращаемый Promise.
  • Тело функции может использовать await для приостановки собственного продолжения без блокировки потока event loop.

Зачем это спрашивают: Интервьюер проверяет, связываете ли вы async-функции с возвратом Promise и семантикой отклонения.

Закрытые вопросы

  • 21

    Что делает await?

    async
  • 22

    Как обрабатываются ошибки с async и await?

    async
  • 23

    Что такое необработанное отклонение Promise?

    promises
  • 24

    Как CommonJS объявляет и загружает модули?

    modules
  • 25

    Как ECMAScript modules объявляют и загружают модули в Node.js?

  • 26

    Каковы основные различия CommonJS и ESM в Node.js?

    modules
  • 27

    Как Node.js определяет, является ли файл .js модулем CommonJS или ESM?

    modules
  • 28

    Как работает кеширование модулей в Node.js?

    cachingmodules
  • 29

    Чем __dirname и __filename отличаются в CommonJS и ESM?

    modules
  • 30

    Для чего используется package.json?

    npm
  • 31

    Чем отличаются dependencies, devDependencies и peerDependencies?

    dependencies
  • 32

    Что такое npm scripts?

    npm
  • 33

    Что означает semantic versioning в диапазоне npm-зависимости?

    dependenciesnpmversioning
  • 34

    Почему lockfile пакетов важен?

    packaging
  • 35

    Для чего используется npx?

  • 36

    Что предоставляет встроенный модуль fs?

  • 37

    Для чего используются path.join и path.resolve?

    joins
  • 38

    Как встроенный модуль http представляет HTTP-запрос и ответ сервера?

    http
  • 39

    Что такое EventEmitter?

  • 40

    Чем отличаются on, once и removeListener у EventEmitter?

  • 41

    Каковы основные виды streams в Node.js?

  • 42

    Как readable stream может передавать данные?

  • 43

    Что такое backpressure в streams Node.js?

    backpressure
  • 44

    Чем отличаются writable.write и writable.end?

  • 45

    Что такое Buffer в Node.js?

  • 46

    Как текстовые кодировки влияют на преобразование Buffer?

  • 47

    Что такое process.env?

    concurrency
  • 48

    Какие полезные данные и средства управления предоставляет объект process?

    concurrency
  • 49

    Что такое middleware в Express?

    middleware
  • 50

    Чем middleware-функция обработки ошибок Express отличается от обычной?

    middleware
  • 51

    Вы реализуете POST /tasks в Express. Как вы проверите запрос и вернёте корректный ответ о создании?

    validation
  • 52

    GET /users/:id сейчас возвращает 200 с null, если пользователь не найден. Как вы исправите обработчик?

    fundamentals
  • 53

    Как вы безопасно разберёте page и limit для GET /products?page=2&limit=20?

  • 54

    Эндпоинт PATCH /users/:id передаёт req.body в обновление базы через spread. Как сделать его безопасным?

    databaseendpointsspread
  • 55

    Эндпоинт Express принимает очень большие JSON-тела, а некорректный JSON превращается в общий ответ 500. Как это исправить?

    endpointsgenerics
  • 56

    Защищённый маршрут Express получает undefined в req.body, а проверка аутентификации не запускается. В каком порядке вы подключите middleware?

    authmiddlewarefundamentals
  • 57

    Как вы реализуете guard аутентификации, который добавляет req.user для защищённого эндпоинта Express?

    authendpoints
  • 58

    Несколько эндпоинтов возвращают разные JSON-структуры для похожих успешных ответов и ошибок. Как вы приведёте их к единому виду?

    endpoints
  • 59

    Эндпоинт загрузки файлов принимает файлы любого количества и размера и иногда исчерпывает память. Как вы это исправите?

    memoryendpoints
  • 60

    Вам нужно добавить валидацию нового эндпоинта, а в проекте может использоваться Fastify или Express. Как вы выберете подход?

    validationendpoints
  • 61

    Маршрут сначала ждёт getUser(id), а затем getOrders(id), хотя вызовы не зависят друг от друга. Как сократить время ответа?

    async
  • 62

    Promise.all отклоняется, пока маршрут загружает несколько обязательных записей. Как маршруту обработать эту ошибку?

    promises
  • 63

    Дашборд может работать, даже если один из пяти необязательных источников данных недоступен. Как собрать успешные данные и отдельно учесть ошибки?

  • 64

    Код запускает createOrder() и chargePayment(order.id) через Promise.all, но для оплаты нужен ID нового заказа. Как исправить этот поток?

    promises
  • 65

    Нужно обработать 100 000 файлов конкурентно, но одновременный запуск 100 000 Promises перегрузит память и файловую систему. Как ограничить конкурентность?

    promisesconcurrencymemory
  • 66

    Сервис использует callback-версию fs.readFile и ещё одно старое API с error-first callback. Как сделать оба вызова удобными для async и await?

    asynccallbacksapi
  • 67

    Условие if (isAllowed(user)) всегда срабатывает, потому что isAllowed является async-функцией. В чём ошибка и как её исправить?

    async
  • 68

    Функция saveUser вызывает database.save(user) внутри фигурных скобок, но сразу возвращает undefined. Как исправить её, чтобы вызывающий код мог дождаться сохранения и получить ошибку?

    asyncfundamentalsdatabase
  • 69

    Запрос к внешнему API иногда завершается ошибкой. Как добавить повторы, не дублируя записи и не повторяя постоянные ошибки?

    resilienceapi
  • 70

    Как остановить fetch-запрос через три секунды и одновременно позволить вызывающему коду отменить его через AbortController?

  • 71

    Небольшой файл настроек содержит JSON. Как загрузить его, не скрывая ошибки синтаксиса JSON?

  • 72

    Маршрут использует readFile для отправки архива размером в несколько гигабайт. Что вы измените?

  • 73

    Сжатие файла использует source.pipe(gzip).pipe(destination), но иногда зависает или пропускает ошибку. Как это исправить?

  • 74

    Как скачать большой HTTP-ответ на диск, не накапливая всё тело в памяти?

    http
  • 75

    Нужно обрабатывать большой CSV-экспорт по одной записи. Будете ли вы разделять входящие chunks по символу новой строки?

    concurrency
  • 76

    Небольшое приложение иногда оставляет наполовину записанный JSON-файл состояния после сбоя. Как сделать запись достаточно атомарной?

    concurrency
  • 77

    Endpoint получает имя файла и отдаёт файл из директории uploads. Как защититься от path traversal?

    endpoints
  • 78

    Экспорт отчёта вызывает writable.write(chunk) в быстром цикле, и потребление памяти постоянно растёт. Как учесть backpressure?

    backpressurememory
  • 79

    Кириллица повреждается при чтении из stream, а бинарные вложения должны оставаться неизменными. Как выбрать между строками и Buffer?

  • 80

    Endpoint чтения файла должен возвращать not found для отсутствующего файла, но сейчас превращает любую ошибку в 404. Как это исправить?

    endpoints
  • 81

    Вызов через встроенный fetch в Node.js попадает в успешную ветку, когда внешнее API возвращает 500. Как исправить этот поток?

    api
  • 82

    Внешнее API вернуло статус 200 и валидный JSON, но в нём нет обязательного id пользователя. Что должен сделать Node.js-сервис?

    api
  • 83

    Код логирует await response.text(), а затем вызывает response.json(), который падает с ошибкой уже использованного body. Как это исправить?

    async
  • 84

    Как безопасно построить URL с query-параметрами и добавить аутентификацию для запроса к внешнему API?

    queriesapiauth
  • 85

    Существующий Node.js-проект уже использует Axios через общий API-клиент, но теперь доступен встроенный fetch. Что вы выберете для новой внешней интеграции?

    api
  • 86

    Внешнее API возвращает 401 из-за истёкшего access token. Как обновить токен и не создать бесконечный цикл повторов?

    tokensapiresilience
  • 87

    Как получить все результаты из внешнего API с cursor pagination?

    pagination
  • 88

    Почему сервис должен преобразовывать ошибки внешнего API, а не передавать вызывающему коду ошибку fetch или Axios напрямую?

    api
  • 89

    Как ошибки из асинхронного маршрута Express или Fastify должны попадать в централизованный обработчик?

    async
  • 90

    Как не допустить утечки внутренних деталей при ошибке внешнего API через ваш Node.js-эндпоинт?

    endpoints
  • 91

    Команда Node.js сообщает о необработанном отклонении Promise и завершается; как найти и исправить причину, не скрывая ошибку глобально?

  • 92

    Вам достались вложенные callbacks, которые читают файл, разбирают его и сохраняют запись; как вы отрефакторите этот поток?

    refactoringownershipcallbacks
  • 93

    Маршрут Express иногда выбрасывает ошибку 'Cannot set headers after they are sent'; как вы найдёте и исправите проблему?

  • 94

    Один запрос к приложению Express навсегда остаётся в ожидании без ответа; что вы проверите?

  • 95

    Эндпоинт запускает большой вычислительный цикл, после чего все остальные запросы замедляются; как вы решите проблему?

    endpoints
  • 96

    Почему async forEach завершает внешнюю функцию до окончания обновлений базы данных и как это исправить?

    databaseasync
  • 97

    Продакшн-запрос периодически завершается ошибкой; какой контекст вы залогируете для отладки без раскрытия секретов?

    secrets
  • 98

    Как протестировать POST-эндпоинт Express или Fastify, не запуская сервер на реальном порту?

    endpoints
  • 99

    Приложение запускается с некорректным PORT, а позже падает из-за отсутствующего URL базы данных; как улучшить работу с конфигурацией?

    databaseconfig
  • 100

    Как добавить graceful shutdown в HTTP-сервис Node.js, у которого также есть пул соединений с базой данных?

    databasehttplifecycle