patterngoMinor
Event-triggering - Ready, On, Fire
Viewed 0 times
firetriggeringreadyevent
Problem
I have implemented a kind of thing that does event triggering in Golang. This is open-sourced here. Review and suggestions appreciated.
First thing I am trying to implement is adding event listeners to a trigger:
So that when Fire is called all the handler chain will be executed:
But the thing is that don't want to maintain a whole param array for all the handlers' method parameters. How can it be done otherwise?
First thing I am trying to implement is adding event listeners to a trigger:
func (t *trigger) On(event string, task interface{}) error {
if _, ok := t.functionMap[event]; ok {
return errors.New("event already defined")
}
if reflect.ValueOf(task).Type().Kind() != reflect.Func {
return errors.New("task is not a function")
}
t.mu.Lock()
defer t.mu.Unlock()
t.functionMap[event] = task
return nil
}So that when Fire is called all the handler chain will be executed:
func (t *trigger) Fire(event string, params ...interface{}) ([]reflect.Value, error) {
f, in, err := t.read(event, params...)
if err != nil {
return nil, err
}
result := f.Call(in)
return result, nil
}But the thing is that don't want to maintain a whole param array for all the handlers' method parameters. How can it be done otherwise?
Solution
Your description of the problem indicates that each event type can have multiple listeners, but the code only implements one listener for each event. Is this intended?
The
Because Go is
You cannot avoid having the
Additionally, your
Note that reads on the map also need to be sync'd, not just writes.
So, this code:
should be:
Additionally, the
The
params awkwardness is a "feature" of go....Because Go is
- statically compiled
- does not have function signature overloading (you can't have multiple functions with the same name, and differentiate them based on the signature)
You cannot avoid having the
param input arguments to the Fire function. This is a "drawback" of Go. When you become more comfortable with the language you avoid constructs like you have, because they are hard to maintain (anything requiring reflect is probably a bad idea).Additionally, your
sync usage is broken - you need to manage the lock on both the Fire call, and the On call. Your On call is also broken because it has a race condition....Note that reads on the map also need to be sync'd, not just writes.
So, this code:
func (t *trigger) On(event string, task interface{}) error {
if _, ok := t.functionMap[event]; ok {
return errors.New("event already defined")
}
if reflect.ValueOf(task).Type().Kind() != reflect.Func {
return errors.New("task is not a function")
}
t.mu.Lock()
defer t.mu.Unlock()
t.functionMap[event] = task
return nil
}should be:
func (t *trigger) On(event string, task interface{}) error {
// Lock the mutex before reading it.
t.mu.Lock()
defer t.mu.Unlock()
if _, ok := t.functionMap[event]; ok {
return errors.New("event already defined")
}
if reflect.ValueOf(task).Type().Kind() != reflect.Func {
return errors.New("task is not a function")
}
t.functionMap[event] = task
return nil
}Additionally, the
Fire should also have a mutex control (perhaps the t.read(...) function has a lock?)func (t *trigger) Fire(event string, params ...interface{}) ([]reflect.Value, error) {
f, in, err := t.read(event, params...)
if err != nil {
return nil, err
}
result := f.Call(in)
return result, nil
}Code Snippets
func (t *trigger) On(event string, task interface{}) error {
if _, ok := t.functionMap[event]; ok {
return errors.New("event already defined")
}
if reflect.ValueOf(task).Type().Kind() != reflect.Func {
return errors.New("task is not a function")
}
t.mu.Lock()
defer t.mu.Unlock()
t.functionMap[event] = task
return nil
}func (t *trigger) On(event string, task interface{}) error {
// Lock the mutex before reading it.
t.mu.Lock()
defer t.mu.Unlock()
if _, ok := t.functionMap[event]; ok {
return errors.New("event already defined")
}
if reflect.ValueOf(task).Type().Kind() != reflect.Func {
return errors.New("task is not a function")
}
t.functionMap[event] = task
return nil
}func (t *trigger) Fire(event string, params ...interface{}) ([]reflect.Value, error) {
f, in, err := t.read(event, params...)
if err != nil {
return nil, err
}
result := f.Call(in)
return result, nil
}Context
StackExchange Code Review Q#135788, answer score: 3
Revisions (0)
No revisions yet.