1/58
Looks like no tags are added yet.
Name | Mastery | Learn | Test | Matching | Spaced |
---|
No study sessions yet.
В чем заключается проблема толстых контролеров?
Проблема "толстых контроллеров" заключается в том, что они берут на себя слишком много обязанностей, включая бизнес-логику, обработку ошибок, валидацию данных и взаимодействие с базой данных. Это ведет к плохой читаемости, сложности в поддержке и тестировании.
Решение — разделение логики на слои (например, сервисы, репозитории) и использование middleware для валидации.
Приведите примеры протекания абстракций (типичных для ноды)
Протекание абстракций происходит, когда детали реализации становятся заметны на более высоких уровнях абстракции, что делает систему сложнее в использовании.
Пример: работа с потоками в Node.js. В некоторых случаях вам нужно управлять низкоуровневыми деталями, такими как пауза и возобновление потоков, хотя это должно быть скрыто.
readStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes of data.`);
readStream.pause(); // Принудительно управляем потоком
// Как только обработали кусок данных
setTimeout(() => readStream.resume(), 1000); // Возобновляем поток
});
Чем отличается requre от import в node js в деталях?
В Node.js существует два способа импорта модулей: require
и import
. Они работают немного по-разному, и их использование связано с различиями между CommonJS и ECMAScript Modules (ESM). Давайте рассмотрим детально основные отличия.
1. Система модулей: CommonJS vs ECMAScript Modules (ESM)
require
— это часть спецификации CommonJS, которая исторически использовалась в Node.js с момента его создания. CommonJS — это модульная система, которая была разработана для серверного JavaScript до появления официальных ECMAScript модулей.
import
— это часть стандарта ECMAScript Modules (ESM), который является официальной спецификацией для модулей в JavaScript. Этот стандарт был добавлен в язык в ES6 (ES2015) и поддерживается в современных версиях Node.js.
2. Синхронность vs Асинхронность
require
загружает модули синхронно. Это значит, что модуль будет загружен и выполнен немедленно во время вызова require
. Для серверных приложений это обычно не является проблемой, так как модули часто загружаются один раз при запуске приложения.
import
работает асинхронно. Он позволяет загружать модули в фоновом режиме, что может быть полезно в клиентских приложениях или при работе с динамическими импортами, где важна производительность и необходимость уменьшения времени загрузки.
3. Обработка модулей
require
загружает модули во время выполнения программы. То есть, каждый раз, когда вызывается require
, Node.js загружает и кэширует модуль.
import
используется в модульной системе ECMAScript, которая использует строгий статический анализ. Это значит, что import
модули определяются во время компиляции и могут быть анализированы на этапе компиляции, что делает их использование более предсказуемым и структурированным.
4. Динамическая загрузка
require
можно вызывать в любое время, где угодно в коде. Это позволяет динамически загружать модули, основываясь на условиях.
import
в базовом виде работает только на верхнем уровне файла и не может быть использован динамически. Однако динамическую загрузку можно выполнить с помощью import()
, что возвращает промис и позволяет загружать модули динамически:
5. Кэширование
Оба метода кэшируют модули, однако кэширование в require
происходит на уровне одного экземпляра. То есть, если один модуль был загружен с помощью require
, все последующие запросы к этому модулю получат закэшированную версию.
import
также кэширует модули, но кэшируется промис, что обеспечивает асинхронность загрузки.
6. Экспорт и импорт модулей
При использовании require
, модули экспортируются через module.exports
или exports
, а импортируются через const moduleA = require('./moduleA')
.
7. Поддержка в Node.js
require
поддерживается во всех версиях Node.js, так как это изначальная система модулей.
import
изначально не поддерживался в Node.js, но начиная с версии 12, Node.js добавил поддержку ECMAScript модулей. Для использования import
в старых версиях Node.js требовалась установка флага --experimental-modules
, а также нужно было использовать расширение .mjs
или указывать "type": "module"
в файле package.json
.
8. Именование файлов
Файлы CommonJS модулей могут иметь расширение .js
и использовать require
.
Для файлов ECMAScript модулей может использоваться расширение .mjs
, либо если указать "type": "module"
в package.json
, можно использовать расширение .js
.
Зачем нам следующие поля Error: error.cause, error.code, error.message, error.stack?
error.message
: сообщение об ошибке, описывающее проблему.
error.stack
: трассировка стека, которая показывает, где возникла ошибка.
error.code
: код ошибки для идентификации типа ошибки.
error.cause
: дополнительная информация о причине ошибки (вложенные ошибки).
Что можно сделать с for await
на экземпляре request:
IncomingMessage
?
Использование for await
на экземпляре IncomingMessage
позволяет асинхронно итерировать по кускам данных, которые приходят в потоке ответа. Это упрощает обработку данных без необходимости вручную добавлять обработчики событий data
и end
.
. Как Node.js нативно хэширует пароли и в каких случаях нам нужны внешние зависимости для этого?
Node.js предоставляет нативный модуль crypto
, который можно использовать для хэширования паролей, но он не предоставляет готовых решений для работы с алгоритмами вроде bcrypt, которые часто используются для хэширования паролей.
Для простого хэширования можно использовать алгоритмы sha256
или pbkdf2
, встроенные в crypto
, но для более безопасного хэширования, особенно с солью, лучше использовать внешние библиотеки, такие как bcrypt
или argon2
.
Когда можно использовать синхронные версии файловых операций из node:fs
вместо асинхронных, и на что нужно обратить внимание при принятии такого решения?
Синхронные операции с файлами могут быть использованы в сценариях, где блокировка выполнения не является критичной, например, при инициализации приложения или конфигурации, до того, как сервер начнет обрабатывать запросы. Однако в продуктивных средах, где важна высокая производительность и асинхронное выполнение задач, предпочтительнее использовать асинхронные операции.
Важные моменты:
Использование синхронных операций блокирует весь поток и снижает производительность.
Синхронные операции допустимы только в однопоточных сценариях или при инициализации, чтобы избежать задержек в основном потоке.
Предложите лучшие практики для обработки ошибок в асинхронном коде.
Использование try/catch
в асинхронных функциях для перехвата ошибок.
Обработка ошибок на уровне промисов с помощью .catch()
.
Никогда не игнорировать ошибки в асинхронных операциях.
Использование глобальных обработчиков необработанных ошибок, таких как process.on('uncaughtException')
и process.on('unhandledRejection')
, для отслеживания неожиданных ошибок.
Разделение ошибок на уровни (например, ошибки валидации, ошибки сети) для корректной обработки.
Как могут появляться уязвимости в проектах на Node.js? Объясните одну из следующих на выбор: XSS, Path traversal, SQL injection, CSRF. Как предотвратить их?
Path traversal (обход путей) — это уязвимость, которая позволяет злоумышленнику получить доступ к файлам за пределами разрешенной директории, манипулируя переменными файлового пути.
Предотвращение:
Использовать безопасные методы работы с путями, такие как path.join()
и path.resolve()
, чтобы избегать неконтролируемых путей.
Ограничить доступ к разрешенным файлам с помощью белых списков.
Почему нужно делать return await
внутри асинхронных функций и методов, а не возвращать промис?
Использование return await
необходимо, чтобы явно дождаться завершения промиса внутри функции. Это позволяет обработать ошибки с помощью блока try/catch
.
async function fetchData() {
try {
return await someAsyncOperation(); // Ошибка будет обработана здесь
} catch (error) {
console.error('Error:', error);
}
}
Если вернуть промис напрямую (return someAsyncOperation();
), ошибки могут не быть пойманы блоком try/catch
, что приведет к неопределенному поведению в обработке ошибок.
Почему middleware является антипаттерном? И как писать без него?
Middleware не всегда является антипаттерном, но его чрезмерное использование может привести к "лавине" вызовов, усложняющей код и его отладку. Особенно это касается глобальных middleware, которые могут затруднить понимание, какие логики и где применяются.
Альтернатива:
Использование четко разделенных сервисов или контроллеров.
Явная инъекция зависимостей и прямые вызовы нужных методов вместо глобальных обработчиков.
Зачем нужен AbortController
? Приведите примеры API, где он используется.
AbortController
позволяет отменять асинхронные операции, такие как HTTP-запросы или операции ввода-вывода.
const controller = new AbortController();
const signal = controller.signal;
fetch('https://example.com', { signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
}
});
controller.abort(); // Прерывание запроса
Как могут утечь все соединения из пула конекшенов к базе данных и как это предотвратить?
Соединения могут "утечь", если приложение открывает соединения и не закрывает их корректно. Это может произойти из-за забытых вызовов connection.close()
или при неправильной обработке ошибок.
Предотвращение:
Использование connection pooling (пул соединений), чтобы повторно использовать открытые соединения.
Грамотное управление временем жизни соединений.
Использование тайм-аутов для соединений, чтобы автоматически закрывать долго неактивные соединения.
В чем преимущество async/await и промисов перед callback в ноде? Где невозможно обойтись без callback?
Преимущества async/await и промисов перед коллбэками:
Читаемость: Async/await и промисы делают код линейным и легко читаемым, что упрощает понимание и отладку. Коллбэки, наоборот, приводят к сложной вложенности (коллбэк-аду).
Обработка ошибок: В промисах ошибки обрабатываются через .catch()
, а в async/await можно использовать try/catch
, что делает работу с ошибками более понятной по сравнению с вложенными коллбэками.
Композиция: Промисы легче комбинировать, используя методы вроде Promise.all()
, что затруднительно с коллбэками.
Когда коллбэки все еще необходимы:
Обратная совместимость: Многие старые API Node.js до сих пор используют коллбэки.
Событийно-ориентированные модели: В некоторых случаях, например, при использовании событийного механизма (как в EventEmitter), коллбэки являются более естественным подходом.
Функции обратного вызова в сторонних библиотеках, которые могут вызывать события или динамические коллбэки.
Что делать, если в двух частях одного приложения вам нужны разные версии npm зависимостей?
Если в приложении нужны разные версии одной и той же зависимости, можно использовать:
Монорепозитории с использованием Lerna или Yarn Workspaces: Позволяют управлять разными версиями зависимостей в подмодулях (пакетах) одного репозитория.
Установка зависимостей на уровне подпакетов: Внутри одного проекта можно создавать отдельные пакеты, каждый со своими зависимостями и версией библиотек в package.json.
Использование scoped-пакетов или yarn resolutions: Определенные инструменты, такие как Yarn, позволяют указать в конфигурации resolutions
, какая версия пакета должна быть установлена для определенного модуля.
Чем может быть опасно, если зависимость патчит глобальные объекты, классы и прототипы?
Опасности:
Конфликты: Если несколько зависимостей изменяют глобальные объекты, это может привести к неожиданным конфликтам и поломкам кода.
Трудности с отладкой: Изменение глобальных объектов делает код менее предсказуемым и усложняет отслеживание проблем.
Несовместимость с будущими версиями: Обновление Node.js или других зависимостей может сломать патч, что приведет к проблемам с совместимостью.
Для чего нам WebSocket, почему в 2023 брать socket.io плохой вариант и что брать для WebSocket?
WebSocket нужен для обеспечения двусторонней связи между клиентом и сервером в режиме реального времени (например, для чатов, игр или уведомлений).
Почему не стоит брать socket.io в 2023:
socket.io использует "фоллбеки" на другие транспортные протоколы, если WebSocket недоступен, что добавляет ненужную сложность.
Нативный WebSocket API в Node.js уже достаточно развит, и его использование более производительно и проще.
Альтернатива:
Использование встроенного WebSocket API (например, в библиотеке ws
для Node.js). Это дает более прямой доступ к WebSocket-протоколу без фоллбеков и лишней логики.
Что дает флаг --watch
?
Флаг --watch
позволяет Node.js автоматически перезапускать процесс при изменении файлов в проекте. Это полезно при разработке, так как отпадает необходимость вручную перезапускать сервер после каждого изменения.
Чего не хватает в ESM, но есть (поддерживается) в CJS?
Несмотря на то, что ES-модули (ESM) становятся стандартом, есть несколько функций, которые поддерживаются в CommonJS (CJS), но отсутствуют или ограничены в ESM:
Динамическая загрузка модулей:
В CJS можно динамически загружать модули с помощью require()
.
В ESM загрузка модулей через import()
возможна, но она всегда асинхронная и работает через промисы, что добавляет сложности для синхронного кода.
Обработка циклических зависимостей:
CJS может обрабатывать циклические зависимости между модулями. ESM тоже поддерживает циклические зависимости, но их поведение может быть менее предсказуемым и более сложным в отладке.
Возможность условной загрузки модулей:
В CJS можно использовать конструкции вроде if (condition) { require('module') }
.
В ESM все импорты выполняются статически на уровне модулей, условная загрузка сложнее.
Мутация экспорта:
В CJS можно изменять экспортированные значения после их инициализации.
В ESM экспорты являются неизменяемыми, и такая возможность отсутствует.
Кэширование модулей:
Оба типа модулей кэшируются, но require
в CJS может быть перезаписан вручную, что позволяет удалять модули из кэша и загружать их заново, тогда как в ESM этого сделать нельзя напрямую.
Почему Node.js не однопоточный? Докажите, что даже не был однопоточным.
Хотя основной поток исполнения в Node.js однопоточный, сама платформа использует несколько потоков под капотом для выполнения операций ввода-вывода, работы с файлами и сетевыми запросами.
Доказательство:
libuv: Node.js использует библиотеку libuv
, которая управляет пулом потоков для выполнения асинхронных операций (например, файловые операции). Этот пул потоков (обычно 4 по умолчанию) делает Node.js многопоточной на уровне низкоуровневых операций.
worker_threads: С версии Node.js 10.5 можно явно создавать потоки (workers), что позволяет параллельно выполнять тяжелые вычисления в отдельных потоках.
Как связаны node:async_hooks
и AsyncLocalStorage
?
AsyncLocalStorage
— это высокоуровневый API для работы с контекстом асинхронного выполнения. Он построен на базе async_hooks
, низкоуровневого API для отслеживания всех асинхронных операций в Node.js.
AsyncLocalStorage
предоставляет более удобный интерфейс для сохранения и получения данных в рамках одного контекста выполнения (например, отслеживания запроса пользователя).
async_hooks
позволяет детально отслеживать создание, завершение и передачу контекстов между асинхронными вызовами.
Какие в ноде встроенные средства сериализации аналогичны JSON только для бинарной сериализации?
Node.js имеет встроенные средства для работы с бинарной сериализацией данных:
Buffer
— основное средство для работы с бинарными данными. Можно использовать для чтения и записи байтов.
node:buffer
(Buffer API): Предоставляет API для создания и управления бинарными данными. Используется в сетевых приложениях и при работе с файлами.
v8.serialize
/ v8.deserialize
: Инструменты для бинарной сериализации данных в формате, аналогичном JSON, но для бинарных данных, что делает их более эффективными для передачи больших объектов.
. Как следить за изменениями файлов и директорий на диске и какие с этим могут возникать проблемы?
Для отслеживания изменений в файлах и директориях можно использовать:
fs.watch
— отслеживает изменения в файлах или директориях.
fs.watchFile
— периодически проверяет файл на изменения (polling).
chokidar
— популярная внешняя библиотека, которая более надежно обрабатывает события файловой системы.
Проблемы:
Кросс-платформенные отличия: Поведение fs.watch
может различаться между операционными системами, особенно на Windows и Linux.
Высокая нагрузка на CPU: Наблюдение за множеством файлов может значительно нагрузить процессор.
Некорректная работа с большими директориями: На больших директориях fs.watch
может не срабатывать на каждый файл.
Чем заменить deprecated fs.exists
и почему его выпиливают из ноды?
fs.exists
был признан устаревшим, потому что его использование было неочевидным и приводило к race conditions. Вместо этого рекомендуется использовать:
fs.access
— проверяет доступность файла или директории с учетом прав доступа.
Промисифицированная версия fs.promises.access
для асинхронного использования.
. Что такое back pressure для стримов и какая проблема была бы без него?
Back pressure — это механизм, который управляет потоком данных между источником (например, чтение из файла) и приемником (например, запись в другой файл или отправка по сети) в случае, если приемник не успевает обрабатывать данные с той же скоростью, с которой их отправляет источник.
Без back pressure:
Приемник мог бы быть перегружен, что привело бы к утечке памяти или краху приложения.
Пример:
В Node.js, когда запись в поток слишком медленная, поток автоматически переходит в режим ожидания, чтобы источник данных мог замедлиться.
Докажите, что любой модуль в ноде при загрузке оборачивается в функцию и создает замыкание?
В Node.js каждый модуль фактически оборачивается в функцию при его загрузке. Это означает, что переменные и функции, определенные в модуле, замкнуты в своем собственном пространстве имен и недоступны извне, что предотвращает конфликт имен
Где в ноде используется паттерн Revealing Constructor (открытый конструктор, есть много таких мест)?
Паттерн Revealing Constructor заключается в том, что объект создается с приватными переменными, которые становятся доступными через методы, раскрытые конструктором. Этот паттерн часто используется в модулях Node.js для инкапсуляции данных и предоставления определенного API.
Примеры:
http.Server
: Конструктор http.Server
создает объект сервера с внутренними свойствами, к которым доступ возможен только через методы, такие как listen
, close
, и обработчики событий.
crypto.Cipher
и crypto.Decipher
: Эти классы скрывают внутренние детали шифрования/дешифрования, предоставляя методы, которые раскрываются через конструктор.
Этот паттерн позволяет скрыть внутреннюю логику и предотвращает прямой доступ к приватным свойствам.
Как сделать переопределение write для экземпляра Writable
без создания класса наследника?
Можно переопределить метод write
для экземпляра Writable
без создания подкласса, просто присвоив новый метод для конкретного объекта.
В чем причина медленных вызовов из JavaScript кода к аддонам на C, C++ или подключенных через N-API?
Причины медленных вызовов из JavaScript в C/C++ через N-API:
Cost of Context Switching: Вызов из JavaScript в C/C++ требует переключения контекста между движком V8 и машинным кодом, что создает накладные расходы.
Marshalling Data: Когда данные передаются между JavaScript и C++, необходимо выполнить сериализацию и десериализацию данных, что также добавляет задержки.
Garbage Collection: Проблемы могут возникнуть из-за частых операций с памятью, которые инициируют сборку мусора в V8.
Чтобы минимизировать эти проблемы, важно оптимизировать вызовы так, чтобы минимизировать количество переходов между языками и избегать частых операций с большими объемами данных.
Что такое MessagePort
и BroadcastChannel
?
MessagePort
: Часть API worker_threads
, которая используется для передачи сообщений между потоками. Два объекта MessagePort
могут связываться между собой для передачи данных асинхронно через сообщения.
BroadcastChannel
: API для передачи сообщений между разными контекстами выполнения, например, между разными Worker'ами или основным потоком. Все подписчики на один канал будут получать сообщения.
Чем отличаются fs.stat
, fs.fstat
, fs.lstat
?
fs.stat
: Получает информацию о файле или директории по пути. Следует символическим ссылкам, возвращая информацию о реальном файле.
fs.fstat
: Получает информацию о файле, используя файловый дескриптор (fd). Используется для работы с уже открытыми файлами.
fs.lstat
: Похож на fs.stat
, но не следует символическим ссылкам, возвращая информацию о самой ссылке.
Какие вы знаете deprecated API и какова стратегия их вывода из употребления?
Некоторые известные устаревшие API в Node.js:
fs.exists
: Удален в пользу fs.access
, так как его поведение могло приводить к race conditions.
require.extensions
: Устаревший механизм для изменения поведения require()
. Заменен на другие механизмы загрузки модулей.
process.EventEmitter
: Заменен на events.EventEmitter
.
Стратегия вывода из употребления:
Сначала API помечается как устаревший, что выводится в консоль в виде предупреждения.
После нескольких мажорных релизов API может быть полностью удален.
Разработчикам предоставляется достаточно времени на миграцию к альтернативам.
Объясните, как можно написать (или напишите) адаптеры асинхронности promisify
и callbackify
?
promisify
— это адаптер, который преобразует функцию с коллбэком в функцию, возвращающую промис.
function promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) return reject(err);
resolve(result);
});
});
};
}
// Пример использования
const fs = require('fs');
const readFile = promisify(fs.readFile);
readFile('example.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
callbackify
— это адаптер, который преобразует функцию, возвращающую промис, обратно в функцию с коллбэком.
function callbackify(fn) {
return function (...args) {
const callback = args.pop();
fn(...args)
.then(result => callback(null, result))
.catch(error => callback(error));
};
}
// Пример использования
const promiseFn = () => Promise.resolve('data');
const callbackFn = callbackify(promiseFn);
callbackFn((err, result) => {
if (err) return console.error(err);
console.log(result);
});
Почему у event loop есть фазы? Почему мало одной очереди?
Event Loop в Node.js разделен на несколько фаз для более эффективной и предсказуемой обработки различных типов операций (таймеры, I/O, callbacks, и т.д.). Каждая фаза отвечает за обработку определенного типа операций, что позволяет:
Избежать задержек и "засорения" одной глобальной очереди. Операции с разными приоритетами (например, таймеры и I/O) не конкурируют за обработку.
Поддерживать предсказуемый порядок выполнения задач, что важно для асинхронного кода и обратной совместимости.
Одна очередь была бы неэффективной, так как таймеры, I/O операции и коллбэки могли бы блокировать выполнение друг друга.
Чем отличаются микротаски и макротаски?
Макротаски (macrotasks) — это крупные задачи, которые включают в себя выполнение основной работы цикла событий (например, setTimeout
, I/O операции, сетевые запросы).
Пример макротаски: setTimeout()
, setImmediate()
, I/O операции.
Микротаски (microtasks) — это задачи с высоким приоритетом, которые выполняются сразу после завершения текущей макротаски, но до начала новой макротаски. Они чаще используются для внутренних процессов движка, таких как обработка промисов.
Пример микротаски: process.nextTick()
, обработка .then()
у промисов.
Различие: Микротаски всегда выполняются перед макротасками. Например, промисы всегда будут разрешены до выполнения задач, поставленных через setTimeout
.
В чем особенности обработки uncaught exceptions
в Node.js?
Если ошибка не поймана в асинхронном коде, она считается uncaught exception
и может привести к аварийному завершению процесса. Для обработки таких ошибок можно использовать глобальный обработчик:
process.on('uncaughtException', (err) => {
console.error('Unhandled exception:', err);
// Завершение процесса
process.exit(1);
});
Чем отличаются nextTick
, setImmediate
и setTimeout
?
process.nextTick()
: Выполняется в конце текущей фазы Event Loop, до того как Event Loop продолжит выполнение следующей фазы. Используется для обработки задач с высоким приоритетом (внутри текущей фазы).
setImmediate()
: Выполняется на следующем витке Event Loop, после завершения текущей фазы и перед следующими макротасками.
setTimeout()
: Выполняется после заданной задержки (в миллисекундах). Если задержка установлена в 0, задача выполняется на следующей фазе Event Loop после других макротасков.
Зачем есть ref()
и unref()
у таймеров, сокетов, серверов и других подобных классов?
ref()
: Объект (например, таймер или сокет) будет удерживать активный процесс Node.js в работе до тех пор, пока он не завершится.
unref()
: Объект не будет блокировать завершение программы, если это единственное событие, оставшееся в очереди.
. Почему server.connections
сделали deprecated и как теперь получить подключения?
server.connections
был устаревшим, так как не работал корректно в случае использования Keep-Alive или с асинхронными операциями. Для получения списка активных подключений лучше использовать события и хранить количество подключений вручную:
Перечислите основные случаи, приводящие к утечке памяти и как с этим бороться?
Неосвобожденные замыкания: Переменные, замкнутые в функциях, продолжают существовать, даже если они больше не нужны.
Решение: Избегать создания ненужных замыканий и очистка объектов после их использования.
Глобальные объекты: Объекты, записанные в глобальную область видимости, остаются в памяти.
Решение: Использовать переменные локально и избегать глобальных объявлений.
Слушатели событий: Слишком много зарегистрированных обработчиков событий, которые не удаляются.
Решение: Очищать обработчики через removeListener
или использовать once()
.
Кэширование: Неправильное управление кэшом, когда слишком много данных сохраняется в памяти.
Решение: Управлять размером кэша и периодически очищать его.
Чем отличается node:cluster
и node:child_process
? И когда cluster
может становиться узким местом?
node:child_process
: Позволяет создавать новые процессы, которые работают независимо от основного. Можно обмениваться сообщениями через IPC.
Пример: выполнение тяжелых операций в отдельном процессе.
node:cluster
: Используется для создания нескольких воркеров, которые могут обрабатывать запросы параллельно. Воркеры разделяют сетевые соединения и часто используются для улучшения производительности HTTP-серверов.
Узкое место cluster
:
Поскольку все воркеры делят одно и то же сетевое соединение, производительность может снижаться при большом количестве запросов. В таких случаях лучше использовать балансировщики нагрузки или другие методы распределения нагрузки.
В каких случаях нужно отключать автоматическую сборку мусора и брать ее вызов в свои руки?
В большинстве случаев отключать автоматическую сборку мусора не рекомендуется. Однако, если ваше приложение сталкивается с большими пиками использования памяти (например, работа с большими файлами или буферами), можно вручную вызвать сборку мусора для освобождения памяти между операциями.
if (global.gc) {
global.gc();
} else {
console.log('GC not exposed');
}
Для включения ручного управления сборкой мусора запустите Node.js с флагом --expose-gc
.
Применение: Используйте ручную сборку мусора для освобождения больших объемов памяти после выполнения крупных задач.
Как сбросить кеш require
для определенной библиотеки? Как быть в случае ESM?
Для сброса кэша модуля, загруженного через require
, можно удалить его из кэша вручную:
delete require.cache[require.resolve('module-name')];
В случае ESM (ES-модули), кэширование управляется движком V8, и прямого способа сброса кэша через import
не существует. Одним из решений может быть перезапуск процесса Node.js, если требуется полная перезагрузка модулей.
Откуда берутся идентификаторы __dirname
и __filename
, require
и import
, fetch
и Array
?
__dirname
и __filename
: Это глобальные переменные, доступные в каждом модуле Node.js. Они предоставляются Node.js и инкапсулируются при загрузке каждого модуля, что создаёт замыкание, включающее эти переменные.
require
и import
: В CommonJS (CJS) require
— это встроенная функция загрузки модулей, тогда как import
является частью спецификации ES-модулей и реализована в движке V8.
fetch
: В Node.js fetch
появился в последних версиях как часть стандарта Web API, но в браузерах он встроен изначально.
Array
: Это глобальный встроенный объект JavaScript, предоставляемый самим движком V8 и доступный в любой среде выполнения JavaScript.
. Почему следует отказаться от использования библиотеки node:url
?
Библиотека node:url
считается устаревшей и менее удобной по сравнению с новыми стандартными Web API, такими как URL
, который стал доступен нативно в Node.js. Новый API:
Более современный и совместимый с браузерной версией.
Предоставляет более мощные и удобные функции для работы с URL.
const myUrl = new URL('https://example.com/path?name=example');
console.log(myUrl.searchParams.get('name')); // 'example'
Чем отличаются cpu-intensive, ram-intensive и io-intensive задачи?
CPU-intensive задачи требуют много вычислительных ресурсов процессора.
Пример: Расчет больших массивов данных, шифрование, сжатие данных, сложные математические вычисления.
RAM-intensive задачи требуют много оперативной памяти.
Пример: Обработка больших наборов данных в памяти, работа с большими файлами, кэширование.
I/O-intensive задачи требуют много операций ввода-вывода.
Пример: Чтение/запись файлов, сетевые запросы, операции с базой данных.
. Почему не нужно использовать process.on('multipleResolves', handler)
?
Использование process.on('multipleResolves')
может свидетельствовать о том, что есть ошибки в коде, где промис разрешается или отклоняется несколько раз. Хотя этот обработчик позволяет отлавливать такие ошибки, лучше всего решать проблему на уровне кода:
Избегать многократного вызова resolve
или reject
для одного промиса.
Исправить логику, которая приводит к таким ситуациям, вместо того чтобы полагаться на обработчик событий.
. Расскажите, на что влияет опция noDelay
у серверов, метод setNoDelay
у request
и socket
?
setNoDelay(true)
отключает алгоритм Nagle, который объединяет маленькие пакеты данных в один большой для оптимизации сети. Отключение этого алгоритма может ускорить отправку данных, если отправляются небольшие куски, такие как интерактивные команды.
Когда использовать: Если важно немедленно отправить данные, например, в реальных временных приложениях, таких как онлайн-игры или чаты.
Для чего используется модуль node:perf_hooks
? И может ли он работать с воркерами?
Модуль node:perf_hooks
используется для измерения производительности внутри Node.js приложений. Он предоставляет API для создания меток времени и сбора данных о производительности.
const { performance } = require('node:perf_hooks');
performance.mark('start');
setTimeout(() => {
performance.mark('end');
performance.measure('My Timer', 'start', 'end');
}, 1000);
Что вы думаете про экспериментальное API итерируемых методов (filter, map, reduce...) у стримов?
Экспериментальное API для работы с потоками (stream
) с использованием методов, подобных Array
(например, filter
, map
, reduce
), — это многообещающая функциональность, которая делает работу с потоками более декларативной и похожей на работу с массивами.
Преимущества:
Лаконичный код: Методы map
, filter
и другие позволяют работать с потоками данных более декларативно, упрощая написание кода и улучшая его читабельность.
Унифицированность: Программисты могут использовать уже знакомые методы, которые они используют с массивами, теперь и с потоками.
Недостатки (пока экспериментально):
Производительность может быть хуже, чем при использовании низкоуровневых операций.
Поскольку API экспериментальное, оно может изменяться в будущих версиях Node.js, что требует осторожности при его использовании в продакшене.
const { Readable } = require('stream');
const readable = Readable.from([1, 2, 3, 4, 5])
.map(x => x * 2)
.filter(x => x > 5);
readable.on('data', chunk => {
console.log(chunk); // 6, 8, 10
});
Какие вы знаете способы интернационализации приложений?
i18n библиотеки (например, i18next
, node-polyglot
):
Поддержка динамических переводов.
Поддержка множественных языков и локализаций.
Пример: переключение между языками в зависимости от предпочтений пользователя.
Поддержка через JSON-файлы:
Хранение переводов и локализованных строк в отдельных JSON-файлах.
Поддержка структуры данных для множественных языков.
Использование ICU (International Components for Unicode):
Node.js поддерживает ICU для работы с локалями, форматированием чисел, дат и валют через API Intl
.
Использование внешних API:
Интеграция с сервисами для перевода контента, такими как Google Translate или другие коммерческие платформы.
Используете ли вы встроенный test runner и библиотеку
Да, встроенный тест-раннер Node.js, который доступен с версии 18, является легковесным и простым способом для написания модульных тестов. Он полезен для небольших проектов или для быстрого создания тестов без установки внешних библиотек.
Преимущества:
assert
: Простая встроенная библиотека для проверок в тестах.
Test Runner: Интегрированная среда для выполнения тестов без установки внешних библиотек.
Какие вы использовали ключи при запуске ноды?
Некоторые полезные ключи для запуска Node.js:
--inspect
и --inspect-brk
:
Для отладки через DevTools.
--inspect-brk
останавливает выполнение в начале.
--max-old-space-size
:
Для увеличения памяти, выделенной V8 (например, для работы с большими объектами или данными).
Пример: node --max-old-space-size=4096 server.js
.
--trace-warnings
:
Для вывода стека предупреждений.
--experimental-modules
:
Для работы с экспериментальными функциями ES-модулей в предыдущих версиях Node.js.
--trace-gc
:
Для отслеживания работы сборщика мусора, полезно для анализа производительности.
Как сдампить хип процесса и что с ним дальше делать?
Создание дампа памяти:
Включить флаг --inspect
и использовать DevTools для снятия дампа памяти.
Либо использовать пакет node-heapdump
для программного создания дампа
Для чего нам модуль, который называется модуль, а именно node:module
?
Модуль node:module
предоставляет низкоуровневые функции для работы с модулями в Node.js. Он позволяет:
Динамически загружать модули.
Работать с путями модулей.
Управлять кэшем модулей.
Как работать с самоподписанными SSL сертификатами? И в чем ограничение их использования?
Для чего в Node.js есть Web Streams API и в чем разница с node:stream
?
Web Streams API был добавлен в Node.js для унификации с API, доступным в браузерах. Это делает работу с потоками более консистентной между серверной и клиентской частью, что облегчает переносимость кода.
Различия с node:stream
:
Web Streams API ориентирован на совместимость с браузерами и предоставление универсального интерфейса для работы с потоками.
Node.js Streams (модуль node:stream
) — это низкоуровневый API, специфичный для Node.js, который поддерживает такие режимы, как Readable
, Writable
, Duplex
, и Transform
.
Основное отличие заключается в том, что node:stream
ориентирован на производительность и поддерживает больше специфичных для Node.js функциональностей, таких как pipe, обработка ошибок и backpressure.
2. Для чего нужны классы Blob
и File
из node:buffer
?
Blob
и File
— это классы, которые предоставляют универсальный интерфейс для работы с файловыми данными и бинарными данными в Node.js, аналогичный их браузерным версиям.
Blob: Позволяет работать с бинарными данными как с неизменяемыми файлами. Используется для отправки данных в веб-запросах или сохранения файлов.
File: Представляет собой файл и содержит метаданные, такие как имя файла и дата последней модификации.
В чем разница моделей прав доступа module-based
и process-based
?
Module-based model: В этом подходе права доступа контролируются на уровне модулей. Каждому модулю предоставляется набор разрешений на доступ к определенным ресурсам (файловой системе, сети и т.д.). Это позволяет изолировать модули друг от друга и предотвращать несанкционированный доступ.
Преимущества: Лучшая изоляция и контроль над доступом для каждого модуля.
Process-based model: Права доступа задаются на уровне всего процесса. Все модули в процессе разделяют одни и те же права, что делает управление более простым, но менее гибким.
Преимущества: Простота управления правами на уровне всего процесса, но меньше возможностей для гибкой изоляции.