← Назад к вопросам

Что такое 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!');
}

Чеклист оптимизации

  1. Профилируй — найди медленные сериализации
  2. Ограничь размер — не сериализуй весь объект
  3. Используй streaming — пиши по частям
  4. Worker threads — если сериализация тяжёлая
  5. Compression — gzip уменьшит размер
  6. Кэширование — не сериализуй одно дважды
  7. Async/await — разбей на chunks с setImmediate