Тест: Мастерство ввода‑вывода NodeJS
Проверьте свои знания о файлах, потоках и буферах
Готовы погрузиться в мир NodeJS I/O? 🌊
Этот тест проверит, насколько вы понимаете операции ввода‑вывода Node, от базовых действий с файловой системой до продвинутых концепций потоков. Мы разберём буферы, кодировки и лучшие практики эффективной обработки данных.
Посмотрим, насколько вы знакомы со своими потоками и буферами! 🚀
Что делает этот код?
const buf = Buffer.alloc(5);console.log(buf);Buffer.alloc(size) создаёт новый Buffer указанного размера, заполненный нулями.
Вывод будет: <Buffer 00 00 00 00 00>
Если хотите создать Buffer со случайными данными, используйте Buffer.allocUnsafe(5).
Что будет выведено?
const buf = Buffer.from([65]);console.log(buf.toString());Числа в массиве представляют ASCII‑коды:
- 65: ‘A’
toString() преобразует эти байты в их строковое представление, используя кодировку UTF-8 по умолчанию.
Какой порядок вывода?
import fs from 'fs';fs.readFile('test.txt', 'utf8', (err, data) => { console.log(data);});console.log('Done');Поскольку readFile асинхронен, код продолжает выполняться, пока файл читается.
Поэтому “Done” будет выведено до содержимого файла.
Чтобы дождаться чтения файла, можно использовать версию на основе Promise:
import { promises as fs } from 'fs';
async function read() { const data = await fs.readFile('test.txt', 'utf8'); console.log(data); console.log('Done');}Что возвращает fs.readFileSync() по умолчанию?
import fs from 'fs';const content = fs.readFileSync('test.txt');fs.readFileSync() возвращает Buffer по умолчанию, если не указана кодировка. Если нужна строка, нужно либо:
- Указать кодировку:
fs.readFileSync('test.txt', 'utf8') - Преобразовать Buffer:
content.toString()
Какой набор событий обычно используется с Readable‑потоками?
Readable потоки генерируют несколько важных событий:
- ‘data’: Когда данные доступны для чтения
- ‘end’: Когда больше нет данных для чтения
- ‘error’: Когда происходит ошибка
- ‘close’: Когда поток и подлежащий ресурс закрыты
const readable = fs.createReadStream('file.txt');readable.on('data', chunk => console.log(chunk));readable.on('end', () => console.log('Done!'));Что делает этот код?
import fs from 'fs';const readable = fs.createReadStream('source.txt');const writable = fs.createWriteStream('dest.txt');readable.pipe(writable);pipe() соединяет читаемый поток с записывающим, автоматически управляя обратным давлением и копируя данные кусками без загрузки всего файла в память.
Это экономит память при работе с большими файлами по сравнению с fs.readFile() и последующим fs.writeFile().
Что делает опция recursive?
import fs from 'fs';fs.mkdirSync('./a/b/c', { recursive: true });recursive: true опция создаёт родительские каталоги, если их нет.
Без этой опции попытка создать ’./a/b/c’ вызовет ошибку, если ’./a’ или ’./a/b’ не существуют.
Это аналогично shell‑команде mkdir -p.
Что будет выведено?
import { Transform } from 'stream';const upperCase = new Transform({ transform(chunk, encoding, callback) { callback(null, chunk.toString().toUpperCase()); }});process.stdin .pipe(upperCase) .pipe(process.stdout);// Input: "hello world"Трансформирующие потоки изменяют данные по мере их прохождения. Здесь каждый кусок данных:
- Преобразуется в строку
- Переводится в верхний регистр
- Передаётся в stdout
Это создаёт конвейер, который переводит весь ввод в верхний регистр.
Сколько раз fs.watch() гарантировано сработает при изменении файла?
import fs from 'fs';fs.watch('test.txt', (eventType, filename) => { console.log(`${filename} was changed`);});// Then modify test.txt oncefs.watch() не гарантирует срабатывание ровно один раз за логическое изменение файла. Часто он срабатывает несколько раз, потому что многие текстовые редакторы:
- Сохраняют в временный файл
- Переименовывают его в целевой файл
Для более надёжного отслеживания рассмотрите:
- Пакет
chokidar - Дебаунсинг колбэка
- Использование
fs.watchFile()(хотя он менее эффективен)
Какой будет вывод?
const buf1 = Buffer.from('Hello');const buf2 = Buffer.from('Hello');console.log(buf1 === buf2);Буферы сравниваются по ссылке, а не по значению. Хотя они содержат одинаковые данные, они являются разными объектами.
Чтобы сравнить содержимое буферов, используйте:
buf1.equals(buf2) // true// orBuffer.compare(buf1, buf2) === 0 // trueКакова основная цель обратного давления в потоках?
Обратное давление — это механизм, который предотвращает переполнение памяти, приостанавливая чтение, когда запись не успевает.
Пример ручного обратного давления:
readable.on('data', (chunk) => { const canContinue = writable.write(chunk); if (!canContinue) { readable.pause(); writable.once('drain', () => readable.resume()); }});pipe() обрабатывает это автоматически!
Что делает этот код?
import fs from 'fs';fs.symlinkSync('target.txt', 'link.txt');symlinkSync создаёт символическую ссылку (как ярлык) на целевой файл.
Ключевые отличия от жёстких ссылок:
- Может ссылаться на каталоги
- Может пересекать файловые системы
- Прерывается, если цель удалена
Чтобы вместо этого создать жёсткую ссылку:
fs.linkSync('target.txt', 'hardlink.txt');В каких режимах могут работать потоки Node.js?
Потоки могут работать в:
- Двоичный режим (по умолчанию): для буферов и строк
- Объектный режим: для любого значения JavaScript
Пример объектного режима:
import { Transform } from 'stream';const objectStream = new Transform({ objectMode: true, transform(chunk, encoding, callback) { callback(null, { value: chunk }); }});Какого типа параметр fd в этом колбэке?
import fs from 'fs';fs.open('test.txt', 'r', (err, fd) => { console.log(typeof fd);});Файловые дескрипторы — это числа, которые уникально идентифицируют открытые файлы в операционной системе.
Первые три файловых дескриптора зарезервированы:
- 0: stdin
- 1: stdout
- 2: stderr
Всегда помните закрывать файловые дескрипторы:
fs.close(fd, (err) => { if (err) throw err;});Сколько байт займет эта строка в UTF-8?
const str = "Hello 🌍";const buf = Buffer.from(str);console.log(buf.length);В UTF-8:
- ASCII‑символы (например ‘Hello ’) занимают 1 байт каждый
- Эмодзи земли 🌍 занимает 4 байта
Итого: 5 (Hello) + 1 (пробел) + 4 (🌍) = 10 байт
Чтобы увидеть байты:
console.log(buf); // <Buffer 48 65 6c 6c 6f 20 f0 9f 8c 8d>Надеюсь, вам понравилось проверять свои знания по NodeJS IO! Хотите продолжить? Загляните в мою Коллекцию викторин для новых задач!