When you dispatch an event, it is identified by a unique name (e.g. "user.created"
). Each listener registered for that name will receive an Event
instance, which may implement additional interfaces for advanced behavior.
The simplest event type is the empty Event
interface. For stoppable events, embed BaseEvent
:
type MyEvent struct {
BaseEvent // provides StopPropagation, IsPropagationStopped
UserID string // custom payload
}
func (e *MyEvent) EventName() string {
return "my_event"
}
e.StopPropagation()
in a listener to prevent further listeners from running.""
to Dispatch
.The EventDispatcher
is the central hub. It maintains per-event listener lists (sharded locks + copy-on-write).
d := eventdispatcher.NewEventDispatcher()
sync.Map
and per-event RWMutex
for minimal contention.Register a listener function with optional priority:
unsubscribe := d.AddListener("user.created",
func(e eventdispatcher.Event) {
evt := e.(*UserCreated)
fmt.Println("User created:", evt.UserID)
},
10, // higher priority executes earlier
)
Parameters:
eventName
(string)Listener
(func(Event))priority
(int, default 0
)Unsubscribe: call the returned unsubscribe()
closure to remove this listener by unique ID.
RemoveListener: remove by function pointer:
d.RemoveListener("user.created", yourFunc)
Define an event struct embedding BaseEvent
or implementing EventName()
:
type OrderPlacedEvent struct {
BaseEvent
OrderID string
}
func (e *OrderPlacedEvent) EventName() string {
return "order.placed"
}
Send the event:
evt := &OrderPlacedEvent{OrderID: "1234"}
d.Dispatch(evt, "") // uses EventName()
Or explicitly:
d.Dispatch(evt, "order.placed")
For dynamic data or simple payloads, use GenericEvent
with pooling:
// Acquire from pool
ge := eventdispatcher.AcquireGenericEvent("dynamic.event", map[string]interface{}{
"foo": 42,
})
// optional: ge.Subject = yourSubject
defer eventdispatcher.ReleaseGenericEvent(ge)
d.AddListener("dynamic.event", func(e eventdispatcher.Event) {
gev := e.(*eventdispatcher.GenericEvent)
fmt.Println("Arg foo:", gev.OffsetGet("foo"))
}, 0)
d.Dispatch(ge, "")
Arguments API:
GetArguments()
, SetArguments(map)
HasArgument
, GetArgument
, SetArgument
OffsetExists
, OffsetGet
, OffsetSet
, OffsetUnset
, GetIterator
Pooling: reuse GenericEvent
instances to reduce GC.
Group multiple event handlers in one struct:
type MySubscriber struct {
called map[string]int
}
func (s *MySubscriber) SubscribedEvents() []eventdispatcher.Subscription {
return []eventdispatcher.Subscription{
{"user.created", s.onUserCreated, 0},
{"order.placed", s.onOrderPlaced, 5},
}
}
sub := &MySubscriber{called: make(map[string]int)}
unsubs := d.AddSubscriber(sub)
// Remove a single listener
unsubs[0]()
// Remove all subscriber listeners
d.RemoveSubscriber(sub)
AddSubscriber
returns individual unsubscribe closures if needed.RemoveSubscriber
unsubscribes all registered handlers for that subscriber.Create a read-only proxy that panics on mutators:
imm := eventdispatcher.NewImmutableEventDispatcher(d)
imm.Dispatch(evt, "name") // works
imm.AddListener("x", nil,0) // panic
Use when you need to expose dispatcher without mutation rights.
EventDispatcher
NewEventDispatcher()
AddListener(name string, fn Listener, prio int) func()
RemoveListener(name string, fn Listener)
Dispatch(event Event, name string) Event
HasListeners(name string) bool
Listeners(name string) []Listener
AddSubscriber(sub Subscriber) []func()
RemoveSubscriber(sub interface{})
GenericEvent
NewGenericEvent(subject interface{}, args map[string]interface{}) *GenericEvent
GetSubject()/SetSubject()
GetArguments()/SetArguments(map[string]interface{})
HasArgument/GetArgument()
SetArgument()
OffsetExists/OffsetGet/OffsetSet/OffsetUnset
GetIterator()
Pooling
AcquireGenericEvent(name string, payload interface{}) *GenericEvent
ReleaseGenericEvent(*GenericEvent)
ImmutableEventDispatcher
NewImmutableEventDispatcher(*EventDispatcher)
Subscriber & Subscription
See full examples and tests in the examples/
directory of the repository.
// Complete sample
package main
import (
"fmt"
"github.com/lemric/eventdispatcher-go"
)
type MsgEvent struct {
eventdispatcher.BaseEvent
Msg string
}
func (e *MsgEvent) EventName() string { return "msg.event" }
func main() {
d := eventdispatcher.NewEventDispatcher()
d.AddListener("msg.event", func(evt eventdispatcher.Event) {
e := evt.(*MsgEvent)
fmt.Println("Received:", e.Msg)
}, 0)
d.Dispatch(&MsgEvent{Msg: "Hello"}, "")
}