Модули и среда исполнения — Примеры
Пример 1 — Одна зависимость, два стиля модулей
Стартовый помощник загружает файл конфигурации. Сначала его написали с функцией, которая загружает модуль по имени, а потом импортировали из более нового файла с явными импортами — и теперь инициализация выполняется дважды. Покажите оба стиля рядом и остановите проект на одном.
// config.cjs
const fs = require('node:fs')
module.exports = {
load: () => JSON.parse(fs.readFileSync('config.json', 'utf8'))
}
Решение
// config.mjs
import { readFile } from 'node:fs/promises'
export const load = async () => JSON.parse(await readFile('config.json', 'utf8'))
Стиль с явными импортами разрешается до запуска программы: инструменты видят привязки, а модуль вычисляется один раз для каждого импортёра. Модуль же, загруженный одним способом и затребованный другим, может вместо этого инициализироваться дважды. При этом динамическая форма остаётся верным инструментом там, где загрузка должна быть условной, — и именно это делает её цель невидимой для статического анализа. Выберите один стиль на зависимость и держитесь его.
Пример 2 — Чтение файла в память, чтобы его отправить
Эндпоинт отдаёт большой файл выгрузки. Маленькие файлы проходят, а на файле продакшен-размера процесс убивают: всё тело держится в памяти до того, как успевает записаться хоть что-то. Переделайте эндпоинт так, чтобы данные уходили кусками.
import { createServer } from 'node:http'
import { readFile } from 'node:fs/promises'
createServer(async (req, res) => {
const body = await readFile('./export.csv') // весь файл, сразу
res.writeHead(200, { 'content-type': 'text/csv' })
res.end(body)
}).listen(3000)
Решение
import { createServer } from 'node:http'
import { createReadStream } from 'node:fs'
createServer((req, res) => {
const source = createReadStream('./export.csv')
source.on('error', (err) => res.destroy(err))
res.on('error', (err) => source.destroy(err))
res.writeHead(200, { 'content-type': 'text/csv; charset=utf-8' })
source.pipe(res)
}).listen(3000)
Куски приходят и записываются по мере чтения, поэтому память остаётся ровной, каким бы большим ни стал файл, и у каждой стороны перекачки есть собственный путь ошибки. Однако пропущенные обработчики дорого обходятся: сбойный поток тихо застревает или течёт вместо громкого падения. Указывайте кодировку и на границе — кракозябры порождает несоответствие той, в которой данные прочитаны, и той, в которой записаны.