← Назад к вопросам
Что такое libuv и какую роль она играет в Node.js?
2.7 Senior🔥 201 комментариев
#Node.js и JavaScript#Кэширование и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему JSON сериализация может заблокировать поток в Node.js и что с этим делать?
JSON.stringify() — это синхронная операция, которая блокирует Event Loop. При сериализации больших объектов, особенно с циклическими ссылками, весь поток Node.js замерзает.
Проблема: JSON.stringify блокирует поток
const http = require('http');
// Огромный объект
const hugeData = {
users: Array(10000).fill(null).map((_, i) => ({
id: i,
name: `User ${i}`,
email: `user${i}@example.com`,
data: 'x'.repeat(1000)
}))
};
const server = http.createServer((req, res) => {
if (req.url === '/data') {
// JSON.stringify блокирует поток на 500ms!
const start = Date.now();
const json = JSON.stringify(hugeData);
const duration = Date.now() - start;
console.log(`Serialization took ${duration}ms`);
res.end(json);
}
});
server.listen(3000);
// Пока идёт сериализация, все остальные запросы ждут!
Почему это плохо?
Thread Timeline:
[Request 1] ████████████████ JSON.stringify (500ms)
[Request 2] ████████ Waiting...
[Request 3] ████████ Waiting...
Все запросы заморожены, пока идёт сериализация.
Решение 1: Использовать worker_threads
import { Worker } from 'worker_threads';
import path from 'path';
// worker.ts
if (require('worker_threads').isMainThread === false) {
const { parentPort } = require('worker_threads');
parentPort.on('message', (obj) => {
const json = JSON.stringify(obj);
parentPort.postMessage(json);
});
}
// main.ts
const serializerWorker = new Worker('./worker.ts');
function stringifyAsync(obj: any): Promise<string> {
return new Promise((resolve) => {
const handler = (json: string) => {
serializerWorker.off('message', handler);
resolve(json);
};
serializerWorker.on('message', handler);
serializerWorker.postMessage(obj);
});
}
const server = http.createServer(async (req, res) => {
if (req.url === '/data') {
// Сериализация в отдельном потоке!
const json = await stringifyAsync(hugeData);
res.end(json);
}
});
Решение 2: Потоковая сериализация
import JSONStream from 'JSONStream';
import es from 'event-stream';
const server = http.createServer((req, res) => {
if (req.url === '/data') {
// Не блокирует поток!
res.setHeader('Content-Type', 'application/json');
// Пишем по частям
const jsonStream = JSONStream.stringify();
jsonStream.write(hugeData);
jsonStream.end();
jsonStream.pipe(res);
}
});
Решение 3: Кастомный replacer с оптимизацией
// Минимизировать объект ДО сериализации
function optimizeForSerialization(obj: any): any {
if (Array.isArray(obj)) {
// Для больших массивов: не сериализуем всё сразу
return obj.slice(0, 1000); // Ограничить размер
}
return obj;
}
const json = JSON.stringify(optimizeForSerialization(hugeData));
Решение 4: Асинхронная сериализация с setImmediate
function stringifyAsync(
obj: any,
chunkSize: number = 10000
): Promise<string> {
return new Promise((resolve) => {
let result = '';
let remaining = JSON.stringify(obj).length;
let index = 0;
const stringified = JSON.stringify(obj);
function processChunk() {
const chunk = stringified.slice(index, index + chunkSize);
result += chunk;
index += chunkSize;
if (index < stringified.length) {
setImmediate(processChunk); // Дать шанс обработать другие операции
} else {
resolve(result);
}
}
processChunk();
});
}
Лучшее решение: Использовать compression и streaming
import compression from 'compression';
import { createReadStream } from 'fs';
const app = express();
app.use(compression()); // Сжать ответ
app.get('/data', (req, res) => {
// Стримируем JSON вместо полной загрузки в память
res.setHeader('Content-Type', 'application/json');
res.write('[');
for (let i = 0; i < 10000; i++) {
const user = { id: i, name: `User ${i}` };
res.write(JSON.stringify(user));
if (i < 9999) res.write(',');
// Дать шанс другим операциям
if (i % 100 === 0) {
await new Promise(resolve => setImmediate(resolve));
}
}
res.write(']');
res.end();
});
Как измерить проблему?
const { performance } = require('perf_hooks');
const largeObj = { data: 'x'.repeat(10000000) };
const start = performance.now();
const json = JSON.stringify(largeObj);
const duration = performance.now() - start;
console.log(`JSON.stringify took ${duration.toFixed(2)}ms`);
if (duration > 100) {
console.warn('WARNING: JSON serialization blocked event loop!');
}
Чеклист оптимизации
- Профилируй — найди медленные сериализации
- Ограничь размер — не сериализуй весь объект
- Используй streaming — пиши по частям
- Worker threads — если сериализация тяжёлая
- Compression — gzip уменьшит размер
- Кэширование — не сериализуй одно дважды
- Async/await — разбей на chunks с setImmediate