> Как Node.js работает с файловой системой и какие библиотеки используются (JavaScript)

Уровень: senior · Роль: backend · Язык: JavaScript · Категория: Технические вопросы

Компании: ЭНИРАН

Стек: Node.js, JavaScript

> Пример ответа

Короткий ответ

Node.js работает с файловой системой через встроенный модуль fs, предоставляющий асинхронные (callback-based и promise-based) и синхронные методы для операций с файлами и директориями. Основные библиотеки - fs/promises для modern async/await, fs-extra для расширенного функционала, chokidar для отслеживания изменений и glob для поиска файлов по шаблонам. Асинхронные методы используют event loop и thread pool libuv для неблокирующего выполнения.

Подробное объяснение

Модуль fs - ядро работы с файловой системой в Node.js. Он построен поверх libuv, которая абстрагирует системные вызовы ОС. Ключевая особенность - асинхронные операции не блокируют основной поток, так как тяжёлые операции (чтение/запись) делегируются thread pool libuv.

Основные группы методов:

  • Асинхронные callback-based (fs.readFile, fs.writeFile) - принимают callback, выполняются неблокирующе
  • Promise-based (fs.promises.readFile, fs.promises.writeFile) - возвращают Promise, удобны с async/await
  • Синхронные (fs.readFileSync, fs.writeFileSync) - блокируют event loop, используются только при старте приложения или в скриптах

Популярные сторонние библиотеки:

  • fs-extra - добавляет ensureDir, copy, move, remove, outputFile и другие удобные методы
  • chokidar - эффективное отслеживание изменений в файловой системе (используется в Webpack, nodemon)
  • glob - поиск файлов по паттернам (**/*.js)
  • graceful-fs - обёртка над fs с очередью и повторными попытками при ошибках

Важные trade-off:

  • Асинхронные методы предпочтительны в production для производительности
  • Синхронные методы могут вызвать starvation event loop при больших файлах
  • Потоки (fs.createReadStream, fs.createWriteStream) обязательны для работы с файлами > 100MB

На практике

В production-приложениях:

  • Используем fs/promises с async/await для читаемости кода
  • Для копирования/перемещения больших директорий применяем fs-extra
  • Для мониторинга изменений конфигов или hot-reload - chokidar
  • Для работы с путями - path модуль (не fs напрямую)
  • Всегда обрабатываем ошибки: ENOENT, EACCES, EMFILE
  • Используем graceful-fs для избежания EMFILE (too many open files)

Пример кода

JAVASCRIPT
import { readFile, writeFile, mkdir } from 'fs/promises';
import { join, dirname } from 'path';
// Асинхронное чтение и запись с обработкой ошибок
async function processConfig(filePath) {
try {
const data = await readFile(filePath, 'utf-8');
const config = JSON.parse(data);
// Создаём директорию, если её нет
const outputDir = dirname(filePath);
await mkdir(outputDir, { recursive: true });
// Записываем обработанные данные
const outputPath = join(outputDir, 'processed.json');
await writeFile(outputPath, JSON.stringify(config, null, 2));
return config;
} catch (err) {
if (err.code === 'ENOENT') {
console.error('Файл не найден:', filePath);
} else if (err.code === 'EACCES') {
console.error('Нет прав доступа');
} else {
console.error('Ошибка обработки:', err.message);
}
throw err;
}
}

Как отвечать на собеседовании

Начни с модуля fs и его трёх API (callback, promise, sync). Упомяни libuv thread pool для асинхронных операций. Перечисли основные сторонние библиотеки с конкретными use cases. Обязательно скажи про обработку ошибок и потоки для больших файлов. Если спросят про производительность - объясни разницу между синхронным и асинхронным подходами и когда что использовать.

Что проверяет интервьюер

  • Понимание event loop и неблокирующего ввода-вывода
  • Знание разницы между callback, promise и sync API
  • Умение выбирать правильный инструмент (потоки vs readFile)
  • Опыт работы с реальными проблемами (EMFILE, ошибки доступа)
  • Знание экосистемы (популярные библиотеки и их назначение)

Типичные ошибки

  • Использование синхронных методов в production API (блокировка event loop)
  • Игнорирование ошибок файловой системы (отсутствие обработки ENOENT, EACCES)
  • Чтение больших файлов целиком вместо использования потоков
  • Неправильное построение путей (ручная конкатенация вместо path.join)
  • Отсутствие обработки EMFILE при большом количестве одновременных операций
  • Использование require('fs') вместо require('fs/promises') для async/await

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью