Criando Um EventEmitter
Lidar com eventos é algo comum em aplicações front-end, estamos constantemente criando, emitindo e escutando eventos, seja eventos de click, scroll, hover, focus, etc… A diversidade de eventos são grandes. Mas, como funciona essa definição e criação de eventos? Nesse post irei criar uma simples implementação de um EventEmitter
responsável por emitir e escutar eventos.
Vamos imaginar o seguinte cenário:
Ao cadastrar um novo usuário, queremos emitir um evento para que seja mostrado uma mensagem de sucesso.
Podemos ter algo parecido com:
const cadastrarNovoUsuario = () => {
const novoUsuario = new Usuario(/* pegar informações de um formulário */)
mandarParaAPI(novoUsuario) // simples implementação para fazer uma requisição do tipo POST
// publicar evento
}
Dado que tudo está funcionando, nosso usuário está sendo criado, a requisição está sendo feita e nossa API está conseguindo salvar o usuário com sucesso, precisamos agora emitir o evento referente a criação de usuário, como podemos fazer isso? Vamos ver uma das possíveis implementações e funcionamento referente à um EventEmitter
.
Entendendo o EventEmitter
Basicamente o que nosso EventEmitter
precisa fazer é receber vários listeners e salvá-los em algum lugar de forma agrupada (categorizada), depois, quando algum evento for emitido, precisamos verificar se existe algum listener para ele e realizar as chamadas para os mesmos. Então podemos definir nossos passos:
- Criar um
EventEmitter
. - Criar uma função para definir um listener.
- Salvar o listener.
- Agrupar os listeners por tópicos (categoria).
- Criar uma função para emitir um evento.
- Buscar todos os listeners desse evento.
- Chamar todos os listener.
Legal, sabemos o que precisamos fazer, hora de por a mão na massa.
Vamos criar nosso EventEmitter
seguindo nossos passos um á um.
Criar um EventEmitter
Vamos começar criando um objeto para representar nosso EventEmitter
:
const EventEmitter = {}
Criar uma função para definir um listener
Para criar novos listeners vamos criar uma nova propriedade que será uma função em nosso objeto:
const EventEmitter = {
listen: () => {}
}
Mas, quando estamos criando um novo listener precisamos dizer para ele qual evento o mesmo ficará escutando e o que ele deve fazer quando esse evento for disparado. Sendo assim, vamos definir dois parâmetros para a nossa função listen
:
const EventEmitter = {
listen: (topic, cb) => {}
}
Veja que criamos dois parâmetros, sendo eles:
topic
: Nome do evento que ele deve ficar aguardando ser emitido.cb
: Função de callback que deve ser executada quando o evento for emitido.
Salvar o listener
Agora que já temos nossa função para criar um novo listener pronta, precisamos salvar esse novo listener em algum lugar para futuramente chamá-lo:
const EventEmitter = {
events: new Map(),
listen: (topic, cb) => {}
}
Veja que definimos uma nova propriedade chamada events
que é do tipo Map
, nesse Map
salvaremos como chave o nome do evento e como valor um array
com todos os seus listeners:
const EventEmitter = {
events: new Map(),
listen: (topic, cb) => {
const oldEvents = EventEmitter.events.get(topic)
if (EventEmitter.events.has(topic)) {
return EventEmitter.events.set(topic, [ ...oldEvents, cb ])
}
return EventEmitter.events.set(topic, [ cb ])
}
}
Calma, irei explicar a nossa função listen
completa:
- Primeiro pegamos todos os eventos já criados para nosso
EventEmitter
e salvamos em uma variável chamadaoldEvents
. - Verificamos se o novo evento à ser escutado já existe em nosso
Map
de eventos. - Caso ele exista, é feito uma concatenação de todos os listeners existentes mais o novo listener e criamos um novo
array
. - Caso ele não existe, é criado um novo
array
com o novo listener.
Com isso, já concluímos mais dois passos da nossa implementação, sendo: Salvar o listener e Agrupar os listeners por tópicos (categoria).
Criar uma função para emitir um evento
Agora precisamos criar uma função para emitir um novo evento:
const EventEmitter = {
events: new Map(),
listen: (topic, cb) => {
const oldEvents = EventEmitter.events.get(topic)
if (EventEmitter.events.has(topic)) {
return EventEmitter.events.set(topic, [ ...oldEvents, cb ])
}
return EventEmitter.events.set(topic, [ cb ])
},
emit: () => {}
}
Veja que adicionamos uma nova propriedade chamada emit
para nosso objeto EventEmitter
, que por hora é apenas uma função simples. Mas, precisamos saber qual evento vamos emitir e quais informações serão enviadas para esse evento, sendo assim, vamos definir dois parâmetros para nossa função emit
:
const EventEmitter = {
events: new Map(),
listen: (topic, cb) => {
const oldEvents = EventEmitter.events.get(topic)
if (EventEmitter.events.has(topic)) {
return EventEmitter.events.set(topic, [ ...oldEvents, cb ])
}
return EventEmitter.events.set(topic, [ cb ])
},
emit: (topic, data) => {}
}
Agora, precisamos pegar todos os eventos e chamar seus listeners:
const EventEmitter = {
events: new Map(),
listen: (topic, cb) => {
const oldEvents = EventEmitter.events.get(topic)
if (EventEmitter.events.has(topic)) {
return EventEmitter.events.set(topic, [ ...oldEvents, cb ])
}
return EventEmitter.events.set(topic, [ cb ])
},
emit: (topic, data) => {
const myListeners = EventEmitter.events.get(topic)
if (Array.isArray(myListeners) && myListeners.length) {
myListeners.forEach(event => event(data))
}
}
}
Vamos dar uma olhaa em nossa função emit
:
- Pegamos todos os listeners para o evento à ser emitido e salvamos em uma variável chamada
myListeners
. - Verificamos se o valor da variável
myListeners
é umarray
e se tem pelo menos um listener. - Percorremos nossa lista de listeners chamando eles e passando as informações como parâmetro.
Com isso conseguimos finalizar todos os passos para a implementação do nosso EventEmitter
.
Testando nosso EventEmitter
Chegou a hora de testar nosso EventEmitter
, primeiro vamos criar um listener:
EventEmitter.listen('USUARIO_SALVO', data => console.log(`Salvando um novo usuário: ${JSON.stringify(data)}`))
Estamos criando um novo listener para o evento USUARIO_SALVO
e como callback vamos apenas logar uma frase com as informações do novo usuário.
Agora, vamos emitir um evento do tipo USUARIO_SALVO
:
const cadastrarNovoUsuario = () => {
const novoUsuario = new Usuario(/* pegar informações de um formulário */)
mandarParaAPI(novoUsuario) // simples implementação para fazer uma requisição do tipo POST
EventEmitter.emit('USUARIO_SALVO', novoUsuario)
}
Veja que no seu console irá aparecer:
Salvando um novo usuário: { /* Informações do usuário */ }
Ou seja, nosso EventEmitter
está funcionando.
Caso queira ver o exemplo completo:
Conclusão
Nesse post entendemos e criamos um simples EventEmitter
que publica e recebe reventos.
E aí, você já havia implementado o seu? Não deixe de comentar.
Caso tenha gostado do post você pode estar assinando nossa newsletter e receber novidades por email.
Abraços, até a próxima.