Skip to main content

readFileSync і його небезпека

1. Що робить readFileSync

javascript
import fs from 'fs'; const data = fs.readFileSync('./data.txt', 'utf8'); console.log(data);

Метод fs.readFileSync() читає файл синхронно - тобто блокує виконання програми, поки операція повністю не завершиться. Node.js не перейде до наступного рядка коду, поки файл не буде прочитано.

2. Node.js - однопоточний

Ось головна причина, чому синхронний I/O небезпечний:

Node.js працює на одному потоці (event loop). Поки одна операція виконується синхронно, весь потік блокується.

Це означає:

  • поки readFileSync() читає файл,
  • жодні інші запити чи події не обробляються: ні HTTP-запити, ні таймери, ні обробники подій.

3. Що реально відбувається при readFileSync()

Асинхронна версія (fs.readFile)

javascript
Event Loop: → Відправив задачу "прочитати файл" у потік (thread pool) → Звільнився і може обслуговувати інші запити → Коли файл готовий - викликає callback

Синхронна версія (fs.readFileSync)

javascript
Event Loop: → Почав читати файл ⏸ Зупинився, чекає результату Більше нічого не робить → Продовжує лише після завершення операції

4. Приклад проблеми

server.js

javascript
import http from 'http'; import fs from 'fs'; const server = http.createServer((req, res) => { const data = fs.readFileSync('./bigfile.txt', 'utf8'); res.end(data); }); server.listen(3000);

Що станеться:

  • Перший запит читає файл 2 секунди - усе інше зупиняється;
  • Інші запити чекають;
  • Сервер "зависає" під навантаженням.

Правильний варіант (асинхронно)

javascript
import http from 'http'; import { readFile } from 'fs/promises'; const server = http.createServer(async (req, res) => { const data = await readFile('./bigfile.txt', 'utf8'); res.end(data); }); server.listen(3000);

Тепер Node може:

  • одночасно читати файл,
  • і обслуговувати інші запити в черзі подій.

5. Приклад "небезпечної" ситуації

javascript
for (let i = 0; i < 1000; i++) { fs.readFileSync('./data.txt', 'utf8'); }

Тут кожна операція чекає попередню. Якщо файл великий - застосунок "зависне" на секунди чи хвилини.

Тоді як асинхронна версія:

javascript
Promise.all( Array(1000).fill().map(() => readFile('./data.txt', 'utf8')) );

читає файли паралельно (у thread pool).

6. Продуктивність у цифрах

МетодЧас (приблизно)Потік блокується
readFileSync()100-300 мс на файлТак
readFile()100-300 мс (у фоні)Ні
readFile (через fs/promises)100-300 мс (у фоні)Ні

На сервері з 1000 запитів на секунду кожен readFileSync() може "заморозити" потік і перетворити Node.js із високопродуктивного на однопотоковий bottleneck.

7. Коли readFileSync() допустимий

Можна використовувати:

  • У скриптах, CLI-утилітах, міграціях (де немає конкуренції запитів);
  • Під час старту застосунку для завантаження конфігурації;
  • У тестах, де потрібно просто отримати дані;
  • Коли продуктивність не критична.

Не можна використовувати:

  • В HTTP-серверах, Express/Fastify маршрутах;
  • В API чи обробниках подій;
  • В асинхронних чергах;
  • У продакшені з одночасними користувачами.

8. Аналогія

Уяви, що Node.js - це офіціант:

  • readFile() - він передає замовлення на кухню й обслуговує інших клієнтів;
  • readFileSync() - він стоїть на місці й чекає, поки кухар приготує страву.

9. Як правильно замінити readFileSync()

Було (погано)Стало (добре)
fs.readFileSync()await fs.promises.readFile()
fs.writeFileSync()await fs.promises.writeFile()
fs.existsSync()await fs.promises.access()

Коротке резюме

ПунктreadFileSync()readFile() / fs.promises.readFile()
ВиконанняСинхроннеАсинхронне
Блокує потікТакНі
Підходить для сервераНіТак
ПростотаТакТак (через await)
ПродуктивністьПогана під навантаженнямВідмінна

Одним реченням

fs.readFileSync() небезпечний у серверних застосунках, тому що блокує весь event loop, заважаючи Node.js обробляти інші запити, і перетворює асинхронний сервер на "завислий" однопоточний процес.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.