Для чего используется Event Emitter в Node.JS?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Event Emitter в Node.js
EventEmitter — это паттерн в Node.js, который позволяет объектам генерировать события и другим объектам слушать эти события. Это основа асинхронного программирования в Node.js.
Базовая идея
Вместо того чтобы функция возвращала результат, она генерирует события. Код, который заинтересован в этих событиях, слушает их.
const EventEmitter = require('events');
const emitter = new EventEmitter();
// Слушаем событие 'message'
emitter.on('message', (data) => {
console.log('Получили сообщение:', data);
});
// Генерируем событие
emitter.emit('message', 'Hello, World!');
// Вывод: "Получили сообщение: Hello, World!"
Основные методы
1. on() — слушать событие
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
// Каждый раз когда event происходит
emitter.on('data', (value) => {
console.log('Данные:', value);
});
emitter.emit('data', 42); // "Данные: 42"
emitter.emit('data', 100); // "Данные: 100"
2. once() — слушать один раз
emitter.once('startup', () => {
console.log('Система запустилась');
});
emitter.emit('startup'); // "Система запустилась"
emitter.emit('startup'); // Не выведет (слушаем только один раз)
3. emit() — генерировать событие
emitter.emit('error', new Error('Что-то пошло не так'));
emitter.emit('user-login', { userId: 123, timestamp: Date.now() });
4. off() / removeListener() — прекратить слушать
const handler = (data) => console.log(data);
emitter.on('message', handler);
emitter.emit('message', 'test'); // Выведет
emitter.off('message', handler); // Удаляем слушатель
emitter.emit('message', 'test'); // Не выведет
5. removeAllListeners() — удалить всех слушателей
emitter.removeAllListeners('message');
// или
emitter.removeAllListeners(); // удалить всех
Практические примеры
1. Читание файла
const { EventEmitter } = require('events');
const fs = require('fs');
class FileReader extends EventEmitter {
read(filename) {
fs.readFile(filename, (err, data) => {
if (err) {
this.emit('error', err);
} else {
this.emit('data', data);
}
});
}
}
const reader = new FileReader();
reader.on('data', (data) => {
console.log('Файл прочитан:', data.toString());
});
reader.on('error', (err) => {
console.error('Ошибка:', err.message);
});
reader.read('file.txt');
2. HTTP сервер (встроенный EventEmitter)
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('OK');
});
// HTTP сервер это EventEmitter!
server.on('request', (req, res) => {
console.log(`Запрос: ${req.method} ${req.url}`);
});
server.on('error', (err) => {
console.error('Ошибка сервера:', err);
});
server.on('connection', (socket) => {
console.log('Новое соединение');
});
server.listen(3000);
3. Pubsub паттерн (издатель-подписчик)
class EventBus extends EventEmitter {}
const eventBus = new EventBus();
// Сервис 1: Заказы
class OrderService {
createOrder(order) {
console.log('Заказ создан:', order);
eventBus.emit('order.created', order);
}
}
// Сервис 2: Уведомления
class NotificationService {
constructor() {
eventBus.on('order.created', (order) => {
console.log('Отправляем уведомление о заказе:', order.id);
});
}
}
// Сервис 3: Аналитика
class AnalyticsService {
constructor() {
eventBus.on('order.created', (order) => {
console.log('Логируем заказ в аналитику');
});
}
}
const orders = new OrderService();
const notifications = new NotificationService();
const analytics = new AnalyticsService();
orders.createOrder({ id: 1, total: 100 });
// Вывод:
// "Заказ создан: { id: 1, total: 100 }"
// "Отправляем уведомление о заказе: 1"
// "Логируем заказ в аналитику"
Встроенные в Node.js EventEmitter'ы
1. Stream (потоки)
const fs = require('fs');
const stream = fs.createReadStream('large-file.txt');
stream.on('data', (chunk) => {
console.log(`Прочитано ${chunk.length} байт`);
});
stream.on('end', () => {
console.log('Файл полностью прочитан');
});
stream.on('error', (err) => {
console.error('Ошибка чтения:', err);
});
2. HTTP request/response
const http = require('http');
const req = http.get('http://api.example.com/data', (res) => {
res.on('data', (chunk) => {
console.log('Получены данные:', chunk.toString());
});
res.on('end', () => {
console.log('Ответ закончился');
});
});
req.on('error', (err) => {
console.error('Ошибка запроса:', err);
});
3. Child Process
const { spawn } = require('child_process');
const child = spawn('ls', ['-la']);
child.stdout.on('data', (data) => {
console.log('Output:', data.toString());
});
child.on('close', (code) => {
console.log('Процесс закончился с кодом:', code);
});
TypeScript + EventEmitter
import { EventEmitter } from 'events';
interface OrderCreatedEvent {
id: number;
total: number;
userId: number;
}
class OrderService extends EventEmitter {
createOrder(data: OrderCreatedEvent) {
console.log('Заказ создан');
this.emit('order.created', data);
}
}
const service = new OrderService();
service.on('order.created', (event: OrderCreatedEvent) => {
console.log(`Заказ ${event.id} создан`);
});
service.createOrder({ id: 1, total: 100, userId: 123 });
Лучшие практики
1. Всегда слушайте 'error' событие
emitter.on('error', (err) => {
console.error('Ошибка произошла:', err);
});
2. Удаляйте слушателей чтобы избежать утечек памяти
const handler = () => { };
emitter.on('event', handler);
// ...
emitter.off('event', handler); // Не забыть!
3. Используйте типизацию (TypeScript)
type EventMap = {
'user.login': (userId: number) => void;
'user.logout': (userId: number) => void;
'error': (err: Error) => void;
};
Зачем нужен EventEmitter
- Decoupling — сервисы не зависят друг от друга
- Асинхронность — работа без блокировки
- Масштабируемость — легко добавить новых слушателей
- Читаемость — понятно что происходит
- Тестируемость — легко мокировать события
EventEmitter — это паттерн, который пронизывает весь Node.js. От streams до HTTP до вашей собственной бизнес-логики.