A small, strongly typed event emitter library for Lua and typed with Teal.
- Designed around strongly typed events using Teal. Events are no longer just strings and bags of data.
- Allows for decoupling logic by subscribing and unsubscribing to events.
- Support for receiving an event at most once.
- Can efficiently forward all events from an Emitter to another Emitter. This is great for things like games that have global events and listeners that should only persist for the lifetime of a specific emitter.
- Listeners can be added or removed while emitting, and removals take effect immediately.
First, require the module:
local Emitter = require("emitter")
An Emitter
is used to publish and subscribe to events.
local emitter = Emitter.new()
Events are strongly typed and require a dedicated type for each kind of event.
For reference, this is the type definition of Event:
local interface Event
type: self
end
Each record that implements an Event must define a type
property of the same
type as the event.
local record WarningEvent is Emitter.Event
message: string
end
This library doesn't have any requirements on how instances of events are created.
An instance of a WarningEvent
can be created using basic table syntax:
local e: WarningEvent = {type = WarningEvent, message = "This is a warning"}
Alternatively, you can provide a constructor for your events.
function WarningEvent.new(message: string): WarningEvent
return {type = WarningEvent, message = message}
end
local e: WarningEvent = WarningEvent.new("This is a warning")
This event can now be emitted and subscribed to.
Listener functions are subscribed to an event using
on<E is Event>(emitter: Emitter, E: event, function(E), config?: ListenerConfig)
.
emitter:on(WarningEvent, function(event: WarningEvent)
print(event.message)
end)
Events are emitted using emit(Event: event)
.
emitter:emit(WarningEvent.new("This is a warning"))
Emitting the event will print "This is a warning".
An optional configuration record can be passed when subscribing to an event
when using on
or once
:
id: string
: An identifier for the listener (defaults to ""). An identifier can be used to unsubscribe the listener by ID. No uniqueness checks are performed on the ID; multiple listeners can use the same ID, allowing events to be grouped.position: "first" | "last"
: Controls whether the listener is added as the last listener for the event using "last" (the default) or the first listener for the event using "first".
off<E is Event>(event: E, listener: Listener<E> | string)
is used to
stop a listener from receiving events.
You can pass in the function that was used to subscribe to the event:
local function onWarning(event: WarningEvent)
print(event.message)
end
emitter:on(WarningEvent, onWarning)
-- Unsubscribe the function.
emitter:off(WarningEvent, onWarning)
When subscribing to an event, an identifier can be given to the listener so that the listener can be unsubscribed by ID rather than the actual function.
emitter:on(WarningEvent, onWarning, { id = "warning" })
emitter:off(WarningEvent, "warning")
IDs can be used for grouping event listeners.
-- While adding listeners, use the same ID to group them.
emitter:on(WarningEvent, b, { id = "print-group" })
emitter:on(WarningEvent, b, { id = "print-group" })
-- Remove both a and b listeners because they both have the id "print-group".
emitter:off(WarningEvent, "print-group")
The once<E is Event>(emitter: Emitter, E: event, function(E))
method can be
used to subscribe to an event and have the listeners automatically removed
after the first event is received.
emitter:once(WarningEvent, function(event: WarningEvent)
print("Once: " .. event.message)
end)
Emitters can forward all events to another Emitter, allowing for a fan-out
pattern. This is done using startForwarding(emitter: Emitter)
:
local forwardEmitter = Emitter.new()
forwardEmitter:on(WarningEvent, function(event: WarningEvent)
print("Child: " .. event.message)
end)
emitter:startForwarding(forwardEmitter)
emitter:emit(WarningEvent.new("This is a warning"))
Will output:
This is a warning
Child: This is a warning
An emitter can be detached from another emitter using
stopForwarding(emitter: Emitter)
:
emitter:stopForwarding(forwardEmitter)
removeAllListeners(event: Event)
is used to remove all listeners for
an event type:
emitter:removeAllListeners(WarningEvent)
reset()
is used to unsubscribe all listeners and stop forwarding all events:
emitter:reset()
Copy and paste:
Copy and paste src/emitter.tl
and/or src/emitter.lua
into your project.
Or use LuaRocks:
luarocks install emitter.tl
The source code is written in Teal and compiled to Lua. The updated and compiled Lua must be part of every code change to the Teal source code. You can compile Teal to Lua and run tests using:
make
This module is free software; you can redistribute it and/or modify it under the terms of the MIT license. See LICENSE for details.