Node.js: EventEmitter
Última atualização: 2026-08-26
Alice é uma engenheira de back-end que desenvolve um sistema de pedidos para comércio eletrônico. Inicialmente, ela fez com que a função de criação de pedidos chamasse diretamente três módulos — envio de e-mails, atualização de estoque e registro em log —, mas isso significava que, toda vez que ela adicionava um novo recurso, precisava modificar o código principal dos pedidos, tornando o sistema cada vez mais frágil. Posteriormente, ela refatorou o código usando o EventEmitter: o módulo de pedidos agora é responsável apenas por acionar o evento order:created, enquanto cada serviço escuta de forma independente, sem interferir uns nos outros. Adicionar “notificações por SMS” requer apenas a inclusão de um ouvinte, sem nenhuma alteração no código principal. Essa arquitetura de publicação-assinatura reduziu os custos de manutenção do sistema em 60%.
1. O que você vai aprender
- Criando emissores de eventos e estendendo-os usando a classe
EventEmitter - Use
on()/emit()/off()/once()/removeAllListeners()para gerenciar eventos - Use
listenerCount()/setMaxListeners()para gerenciar os valores dos alertas de ouvintes e de vazamento de memória - Crie uma classe de evento personalizada e encapsule a lógica de negócios
- Tratamento adequado de eventos
errore exceções não capturadas - Compreender o uso de
EventEmitternos módulos integrados do Node.js - Criação de uma arquitetura fracamente acoplada e orientada a eventos, utilizando o padrão de publicação-assinatura
2. Conceitos básicos do EventEmitter
EventEmitter é a classe fundamental para a arquitetura orientada a eventos do Node.js, localizada no módulo events. Ela mantém um mapeamento entre nomes de eventos e matrizes de funções de escuta. Quando emit() aciona um evento, todas as funções de escuta registradas são executadas sincronicamente na ordem em que foram registradas.
flowchart LR
subgraph Emitter["EventEmitter(Posted by)"]
E1[emit - order:created]
end
subgraph Listeners["Listener(Subscribers)"]
L1[Listener1:Send an Email]
L2[Listener2:Update Inventory]
L3[Listener3:Log Entry]
end
E1 --> L1
E1 --> L2
E1 --> L3
style Emitter fill:#e1f5fe
style Listeners fill:#f3e5f5
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('greet', (name) => {
console.log(`Hello, ${name}!`);
});
emitter.emit('greet', 'Alice');
Hello, Alice!
3. Referência rápida aos métodos comuns do EventEmitter
| Método | Descrição | Valor de retorno |
|---|---|---|
on(event, listener) |
Registra um ouvinte; pode ser registrado várias vezes | Instância de EventEmitter |
once(event, listener) |
Registrar um ouvinte de uso único que é removido automaticamente ao ser acionado | Instância do EventEmitter |
emit(event, ...args) |
Acionar um evento, passar parâmetros | true Possui um ouvinte / false Não possui ouvinte |
off(event, listener) |
Remover um ouvinte especificado | Instância de EventEmitter |
removeListener(event, listener) |
Igual a off(), API antiga |
Instância de EventEmitter |
removeAllListeners([event]) |
Remover ouvintes de todos os eventos ou de eventos específicos | Instância de EventEmitter |
prependListener(event, listener) |
Adicionar um ouvinte ao início da fila | Instância do EventEmitter |
prependOnceListener(event, listener) |
Adicionar um ouvinte de uso único no início | Instância do EventEmitter |
listeners(event) |
Retorna o array de ouvintes de eventos | Function[] |
listenerCount(event) |
Número de ouvintes | number |
setMaxListeners(n) |
Definir o número máximo de ouvintes para um único evento | Instância do EventEmitter |
getMaxListeners() |
Obter o número máximo de ouvintes | number |
eventNames() |
Retornar todos os nomes de eventos registrados | `(string |
rawListeners(event) |
Retorna o ouvinte original marcado com once |
Function[] |
▶ Exemplo: Uso básico de on/emit/off
const EventEmitter = require('events');
const emitter = new EventEmitter();
function onOrderCreated(order) {
console.log(`[Listener] Order created: #${order.id}`);
}
emitter.on('order:created', onOrderCreated);
emitter.emit('order:created', { id: 1001, product: 'Laptop', qty: 2 });
emitter.off('order:created', onOrderCreated);
const hasListeners = emitter.emit('order:created', { id: 1002, product: 'Mouse', qty: 5 });
console.log('Has listeners:', hasListeners);
[Listener] Order created: #1001
Has listeners: false
4. Comparação entre on, once e prependListener
| Características | on() |
once() |
prependListener() |
|---|---|---|---|
| Número de gatilhos | Aciona sempre | Aciona apenas uma vez e, em seguida, é removido automaticamente | Aciona sempre |
| Local de registro | Fim da fila | Fim da fila | Início da fila (executado primeiro) |
| Cenários típicos | Monitoramento contínuo de mensagens | Inicialização realizada apenas uma vez | Processamento de alta prioridade (por exemplo, registro em log) |
| Remoção automática | Não | Sim | Não |
| Corresponde à versão descartável | — | once() |
prependOnceListener() |
▶ Exemplo: “once” é acionado apenas uma vez
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.once('server:ready', (port) => {
console.log(`Server initialized on port ${port}`);
});
emitter.emit('server:ready', 3000);
emitter.emit('server:ready', 3001);
console.log('Second emit had no effect');
Server initialized on port 3000
Second emit had no effect
▶ Exemplo: o prependListener é executado primeiro
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('data', () => console.log('Normal listener'));
emitter.prependListener('data', () => console.log('Priority listener'));
emitter.emit('data');
Priority listener
Normal listener
5. Gerenciamento de ouvintes de eventos
Por padrão, o Node.js permite no máximo 10 ouvintes por evento; ultrapassar esse limite acionará um aviso de vazamento de memória. Esse não é um limite rígido, mas sim um lembrete para que os desenvolvedores verifiquem se não se esqueceram de remover algum ouvinte.
▶ Exemplo: listenerCount e avisos de vazamento de memória
const EventEmitter = require('events');
const emitter = new EventEmitter();
for (let i = 0; i < 12; i++) {
emitter.on('log', () => console.log(`Listener ${i}`));
}
console.log('Listener count:', emitter.listenerCount('log'));
console.log('Max listeners:', emitter.getMaxListeners());
(node:1234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 12 log listeners added. Use emitter.setMaxListeners() to increase limit
Listener count: 12
Max listeners: 10
▶ Exemplo: Ajustando o limite superior com setMaxListeners
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.setMaxListeners(20);
for (let i = 0; i < 15; i++) {
emitter.on('task', () => {});
}
console.log('No warning: max set to', emitter.getMaxListeners());
console.log('Listener count:', emitter.listenerCount('task'));
No warning: max set to 20
Listener count: 15
▶ Exemplo: limpeza com removeAllListeners
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('tick', () => console.log('tick A'));
emitter.on('tick', () => console.log('tick B'));
emitter.on('tock', () => console.log('tock A'));
emitter.removeAllListeners('tick');
console.log('tick listeners:', emitter.listenerCount('tick'));
console.log('tock listeners:', emitter.listenerCount('tock'));
tick listeners: 0
tock listeners: 1
6. Classes de eventos personalizadas
No desenvolvimento prático, é comum herdar de EventEmitter para criar classes de eventos com semântica de negócios, em vez de usar instâncias de EventEmitter diretamente.
▶ Exemplo: Criando uma classe de evento de pedido ao estender a classe EventEmitter
const EventEmitter = require('events');
class OrderEmitter extends EventEmitter {
create(order) {
this.emit('order:created', order);
}
cancel(orderId) {
this.emit('order:cancelled', { orderId, cancelledAt: new Date().toISOString() });
}
ship(orderId, trackingNo) {
this.emit('order:shipped', { orderId, trackingNo });
}
}
const orderEvents = new OrderEmitter();
orderEvents.on('order:created', (order) => {
console.log(`[Email] Confirmation for order #${order.id}`);
});
orderEvents.on('order:created', (order) => {
console.log(`[Inventory] Deduct ${order.qty}x ${order.product}`);
});
orderEvents.on('order:cancelled', ({ orderId }) => {
console.log(`[Refund] Processing refund for #${orderId}`);
});
orderEvents.create({ id: 2001, product: 'Headphones', qty: 3 });
orderEvents.cancel(2001);
[Email] Confirmation for order #2001
[Inventory] Deduct 3x Headphones
[Refund] Processing refund for #2001
7. O error e o tratamento de erros
O EventEmitter possui um evento especial error: se o evento error for acionado, mas não houver ouvintes, o Node.js lançará uma exceção e encerrará o processo. Essa é a regra de segurança mais importante do EventEmitter.
▶ Exemplo: Falha no processo devido à incapacidade de lidar com um erro
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.emit('error', new Error('Database connection failed'));
events.js:291
throw er; // Unhandled 'error' event
^
Error: Database connection failed
at Object.<anonymous> (app.js:4:20)
▶ Exemplo: Como lidar corretamente com o evento error
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('error', (err) => {
console.error(`[Error Handler] ${err.message}`);
});
emitter.emit('error', new Error('Database connection failed'));
console.log('Process continues running');
[Error Handler] Database connection failed
Process continues running
8. Comparação entre abordagens orientadas a eventos, callbacks e promessas
| Recurso | Orientado a eventos (EventEmitter) | Callback | Promise / async-await |
|---|---|---|---|
| Modo de comunicação | Um para muitos (publicação-assinatura) | Um para um | Um para um |
| Número de acionadores | Pode ser acionado várias vezes | Aciona apenas uma vez | É resolvido apenas uma vez |
| Cenários típicos | Transmissão de mensagens, mudanças de estado | Conclusão de operações de E/S | Resultados finais de operações assíncronas |
| Acoplamento | Baixo (o editor não conhece o assinante) | Alto (o chamador deve conhecer o destinatário da chamada de retorno) | Médio (encadeamento ou await) |
| Cancelável | Pode ser removido com off() | Não cancelável | Não cancelável (pode ser ignorado) |
| Suporte integrado | Módulo events |
Convenções globais | Linguagem integrada |
| Tratamento de erros | error Evento |
Callback de prioridade de erro | .catch() / try-catch |
9. Módulos integrados do Node.js que utilizam o EventEmitter
Muitos dos módulos integrados do Node.js herdam de EventEmitter; quase todos os objetos de fluxo, servidor e processo são emissores de eventos.
| Módulo/Objeto | Herda de EventEmitter | Eventos comuns |
|---|---|---|
net.Server |
Sim | connection, close, error |
http.Server |
Sim | request, connection, close |
stream.Readable |
Sim | data, end, error, close |
stream.Writable |
Sim | drain, finish, error, close |
net.Socket |
Sim | data, connect, end, error |
process |
Sim | exit, uncaughtException, SIGINT |
child_process.ChildProcess |
Sim | exit, message, error, close |
fs.watch() |
Voltar ao EventEmitter | change, error |
▶ Exemplo: http.Server usando EventEmitter
const http = require('http');
const server = http.createServer();
server.on('request', (req, res) => {
console.log(`[Request] ${req.method} ${req.url}`);
res.end('OK');
});
server.on('connection', (socket) => {
console.log(`[Connection] New client from ${socket.remoteAddress}`);
});
server.listen(3000, () => {
console.log('Server listening on port 3000');
});
Server listening on port 3000
[Connection] New client from ::ffff:127.0.0.1
[Request] GET /
▶ Exemplo: Fluxo legível usando o EventEmitter
const { Readable } = require('stream');
const readable = Readable.from(['Hello', ' ', 'World']);
readable.on('data', (chunk) => {
console.log(`[Data] Received: "${chunk}"`);
});
readable.on('end', () => {
console.log('[End] No more data');
});
[Data] Received: "Hello"
[Data] Received: " "
[Data] Received: "World"
[End] No more data
10. Exemplo abrangente: Agendador de tarefas orientado a eventos
Crie uma classe TaskScheduler que suporte o registro de tarefas, o acionamento de eventos e as respostas de vários ouvintes. Simule a cadeia de notificações que ocorre após a criação de uma tarefa em um cenário real.
const EventEmitter = require('events');
class TaskScheduler extends EventEmitter {
constructor() {
super();
this.tasks = new Map();
this.nextId = 1;
}
addTask(name, payload) {
const id = this.nextId++;
const task = {
id,
name,
payload,
createdAt: new Date().toISOString(),
status: 'pending'
};
this.tasks.set(id, task);
this.emit('task:added', task);
return task;
}
startTask(id) {
const task = this.tasks.get(id);
if (!task) {
this.emit('error', new Error(`Task #${id} not found`));
return;
}
task.status = 'running';
task.startedAt = new Date().toISOString();
this.emit('task:started', task);
}
completeTask(id, result) {
const task = this.tasks.get(id);
if (!task) {
this.emit('error', new Error(`Task #${id} not found`));
return;
}
task.status = 'completed';
task.result = result;
task.completedAt = new Date().toISOString();
this.emit('task:completed', task);
}
failTask(id, reason) {
const task = this.tasks.get(id);
if (!task) {
this.emit('error', new Error(`Task #${id} not found`));
return;
}
task.status = 'failed';
task.reason = reason;
task.failedAt = new Date().toISOString();
this.emit('task:failed', task);
}
getStats() {
const stats = { total: this.tasks.size, pending: 0, running: 0, completed: 0, failed: 0 };
for (const task of this.tasks.values()) {
stats[task.status]++;
}
return stats;
}
}
const scheduler = new TaskScheduler();
scheduler.on('error', (err) => {
console.error(`[ERROR] ${err.message}`);
});
scheduler.on('task:added', (task) => {
console.log(`[Logger] Task #${task.id} "${task.name}" added at ${task.createdAt}`);
});
scheduler.on('task:added', (task) => {
console.log(`[Notifier] New task available: ${task.name}`);
});
scheduler.on('task:started', (task) => {
console.log(`[Executor] Task #${task.id} is now running...`);
});
scheduler.on('task:completed', (task) => {
console.log(`[Reporter] Task #${task.id} completed with result: ${task.result}`);
console.log(`[Cleaner] Releasing resources for task #${task.id}`);
});
scheduler.on('task:failed', (task) => {
console.log(`[Alerter] Task #${task.id} failed: ${task.reason}`);
});
console.log('=== Adding Tasks ===');
const t1 = scheduler.addTask('Data Import', { source: 'api.example.com', rows: 5000 });
const t2 = scheduler.addTask('Report Generation', { format: 'PDF', quarter: 'Q4' });
console.log('\n=== Starting Tasks ===');
scheduler.startTask(t1.id);
scheduler.startTask(t2.id);
console.log('\n=== Completing / Failing Tasks ===');
scheduler.completeTask(t1.id, '5000 rows imported successfully');
scheduler.failTask(t2.id, 'PDF renderer service unavailable');
console.log('\n=== Stats ===');
console.log(scheduler.getStats());
console.log('\n=== Invalid Operation ===');
scheduler.startTask(999);
=== Adding Tasks ===
[Logger] Task #1 "Data Import" added at 2025-07-03T10:00:00.000Z
[Notifier] New task available: Data Import
[Logger] Task #2 "Report Generation" added at 2025-07-03T10:00:01.000Z
[Notifier] New task available: Report Generation
=== Starting Tasks ===
[Executor] Task #1 is now running...
[Executor] Task #2 is now running...
=== Completing / Failing Tasks ===
[Reporter] Task #1 completed with result: 5000 rows imported successfully
[Cleaner] Releasing resources for task #1
[Alerter] Task #2 failed: PDF renderer service unavailable
=== Stats ===
{ total: 2, pending: 0, running: 0, completed: 1, failed: 1 }
=== Invalid Operation ===
[ERROR] Task #999 not found
❓ Perguntas Frequentes
P: Qual é a diferença entre o EventEmitter e os eventos do navegador? R: O EventEmitter do Node.js é executado de forma síncrona, com os ouvintes sendo executados na ordem em que foram registrados; os eventos DOM do navegador são assíncronos, passam pelas fases de captura e propagação e possuem uma estrutura de objeto de evento diferente da do Node.js.
P: O que acontece se você se esquecer de monitorar o evento
error? R: Se nenhum monitorador doerrorestiver registrado quando oemit('error')for chamado, o Node.js lançará o erro como uma exceção não capturada, e o processo será encerrado imediatamente (a menos que oprocess.on('uncaughtException')esteja definido).
P: Qual é o número máximo de ouvintes que um evento pode ter? R: O limite padrão é 10. Ultrapassar esse limite acionará um aviso de vazamento de memória (mas não bloqueará a execução). Isso pode ser ajustado por meio de
emitter.setMaxListeners(n); definir esse valor como 0 remove o limite.
P: Qual é o valor de retorno de
emit()? R: Se o evento tiver pelo menos um ouvinte, ele retornatrue; se não tiver ouvintes, retornafalse. Isso pode ser usado para determinar se o evento foi processado.
P: Como posso garantir que um ouvinte seja removido automaticamente após ser acionado apenas uma vez? R: Use o método
once()para registrar o ouvinte; ele será removido automaticamente após ser acionado uma vez. Isso é adequado para cenários como inicialização e sinais únicos.
P: Qual é a diferença entre
off()eremoveListener()? R: Eles funcionam exatamente da mesma forma.off()é um novo alias introduzido no Node.js v10removeListener()que oferece uma sintaxe mais concisa; recomenda-se usaroff()em códigos novos.
P: A que se refere
thisem um listener? R: Ao usar funções-seta do ES6,thisherda o escopo externo; ao usar funções comuns,thisse refere à instânciaEventEmitter, a menos que tenha sido vinculada a outro objeto por meio debind().
📖 Resumo
- Conceitos-chave e como aplicá-los
- Conceitos básicos e uso do EventEmitter
- Conceitos básicos e uso do EventEmitter: Referência rápida aos métodos mais comuns
- Conceitos-chave e uso de “on”, “once” e “prependListener”: uma comparação
- Conceitos básicos e uso de ouvintes de eventos
- Conceitos básicos e uso de classes de eventos personalizados
- Conceitos básicos e uso do
errorpara tratamento de eventos e erros - Conceitos-chave e uso de programação orientada a eventos, callbacks e promessas: uma comparação
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.