├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── goemitter.go └── goemitter_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mohammed Al Ashaal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emitter 2 | ======== 3 | 4 | An event-emitter package for `Go` versions >= 1.2, this package is inspired by `javascript` event-emitter library . 5 | 6 | # Author 7 | ======= 8 | Mohammed Al Ashaal, `just a full-stack developer` . 9 | i'm on [twitter](https://www.twitter.com/m7medalash3al), [facebook](https://www.facebook.com/alash3al) and [github](https://www.github.com/alash3al) 10 | 11 | # Installation 12 | ============ 13 | 14 | `go get github.com/alash3al/goemitter` 15 | 16 | # Learn By Examples ? 17 | ==================== 18 | 19 | ```go 20 | package main 21 | 22 | import( 23 | "fmt" 24 | "github.com/alash3al/goemitter" 25 | ) 26 | 27 | func main(){ 28 | 29 | // just a shortcut 30 | echo := fmt.Println 31 | 32 | // initialize a new instance ? 33 | emitter := Emitter.Construct() 34 | 35 | // register a new listener for an event 36 | // Yep, the listener must be in this template "func(...interface{})" 37 | // the args are the arguments passed to the listener 38 | emitter.AddListener("myevent", func(args ... interface{}){ 39 | echo("this is the listener argument -> ", args[0]) 40 | }) 41 | 42 | // just an alias of AddListener 43 | emitter.On(/* ... */) 44 | 45 | // how about registering a one time listener 46 | // that will be removed after the first run 47 | emitter.Once("myevent", func(args ...interface{}){ 48 | echo("this will run just once") 49 | }) 50 | 51 | // run all event-listeners 'in synchronous mode' 52 | // and pass nil as arguments list 53 | // anything else, the arguments must be []interface{} 54 | emitter.EmitSync("myevent", nil) 55 | 56 | // anything else, the arguments must be []interface{} 57 | // i.e 58 | emitter.EmitSync("myevent", []interface{}{"first arg", "second arg"}) 59 | 60 | // but i want to emit listeners using goroutines ! 61 | // ok, don't panic() ;) 62 | emitter.EmitAsync(/* the same as EmitSync, just change to EmitAsync :) */) 63 | 64 | // defining a listener 65 | fn := func(args ...interface{}){ 66 | // your code 67 | } 68 | 69 | // register 70 | emitter.On("myevent", fn) 71 | 72 | // now remove it 73 | emitter.RemoveListener("myevent", fn) 74 | 75 | // remove all listeners from an event ? 76 | emitter.RemoveAllListeners("myevent") 77 | 78 | // remove all listeners from all events ? 79 | emitter.RemoveAllListeners() 80 | 81 | // return a slice with "Listener" struct of an event ? 82 | // What is Listener struct? - just continue, don't worry 83 | emitter.Listeners("myevent") // nil if empty 84 | 85 | // return the count of listeners of an event 86 | emitter.ListenersCount("myevent") 87 | 88 | // now lets know about the internal structs 89 | // 1)- Emitter 90 | // It contains a map of event => listeners 91 | // src ? - ok 92 | type Emitter struct { 93 | listeners map[interface{}][]Listener 94 | } 95 | 96 | // 2)- Listener 97 | // When you register a callback 98 | // its automatically passed in a new `Listener` 99 | // and whether it is a one-time listener or not 100 | type Listener struct { 101 | callback func(...interface{}) 102 | once bool 103 | } 104 | } 105 | ``` 106 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/moleculer-go/goemitter 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /goemitter.go: -------------------------------------------------------------------------------- 1 | // An EventEmitter package for go 1.2 + 2 | // Author: Mohammed Al Ashaal 3 | // License: MIT License 4 | // Version: v1.0.0 5 | package Emitter 6 | 7 | import ( 8 | "reflect" 9 | "sync" 10 | ) 11 | 12 | // Emitter - our listeners container 13 | type Emitter struct { 14 | listeners map[interface{}][]Listener 15 | mutex *sync.Mutex 16 | } 17 | 18 | // Listener - our callback container and whether it will run once or not 19 | type Listener struct { 20 | callback func(...interface{}) 21 | once bool 22 | } 23 | 24 | // Construct() - create a new instance of Emitter 25 | func Construct() *Emitter { 26 | return &Emitter{ 27 | make(map[interface{}][]Listener), 28 | &sync.Mutex{}, 29 | } 30 | } 31 | 32 | // Destruct() - free memory from an emitter instance 33 | func (self *Emitter) Destruct() { 34 | self = nil 35 | } 36 | 37 | // AddListener() - register a new listener on the specified event 38 | func (self *Emitter) AddListener(event string, callback func(...interface{})) *Emitter { 39 | return self.On(event, callback) 40 | } 41 | 42 | // On() - register a new listener on the specified event 43 | func (self *Emitter) On(event string, callback func(...interface{})) *Emitter { 44 | self.mutex.Lock() 45 | if _, ok := self.listeners[event]; !ok { 46 | self.listeners[event] = []Listener{} 47 | } 48 | self.listeners[event] = append(self.listeners[event], Listener{callback, false}) 49 | self.mutex.Unlock() 50 | 51 | self.EmitSync("newListener", []interface{}{event, callback}) 52 | return self 53 | } 54 | 55 | // Once() - register a new one-time listener on the specified event 56 | func (self *Emitter) Once(event string, callback func(...interface{})) *Emitter { 57 | self.mutex.Lock() 58 | if _, ok := self.listeners[event]; !ok { 59 | self.listeners[event] = []Listener{} 60 | } 61 | self.listeners[event] = append(self.listeners[event], Listener{callback, true}) 62 | self.mutex.Unlock() 63 | 64 | self.EmitSync("newListener", []interface{}{event, callback}) 65 | return self 66 | } 67 | 68 | // RemoveListeners() - remove the specified callback from the specified events' listeners 69 | func (self *Emitter) RemoveListener(event string, callback func(...interface{})) *Emitter { 70 | return self.removeListenerInternal(event, callback, false) 71 | } 72 | 73 | func (self *Emitter) removeListenerInternal(event string, callback func(...interface{}), suppress bool) *Emitter { 74 | if _, ok := self.listeners[event]; !ok { 75 | return self 76 | } 77 | for k, v := range self.listeners[event] { 78 | if reflect.ValueOf(v.callback).Pointer() == reflect.ValueOf(callback).Pointer() { 79 | self.listeners[event] = append(self.listeners[event][:k], self.listeners[event][k+1:]...) 80 | if !suppress { 81 | self.EmitSync("removeListener", []interface{}{event, callback}) 82 | } 83 | return self 84 | } 85 | } 86 | return self 87 | } 88 | 89 | // RemoveAllListeners() - remove all listeners from (all/event) 90 | func (self *Emitter) RemoveAllListeners(event interface{}) *Emitter { 91 | self.mutex.Lock() 92 | defer self.mutex.Unlock() 93 | 94 | if event == nil { 95 | self.listeners = make(map[interface{}][]Listener) 96 | return self 97 | } 98 | if _, ok := self.listeners[event]; !ok { 99 | return self 100 | } 101 | delete(self.listeners, event) 102 | return self 103 | } 104 | 105 | // Listeners() - return an array with the registered listeners in the specified event 106 | func (self *Emitter) Listeners(event string) []Listener { 107 | if _, ok := self.listeners[event]; !ok { 108 | return nil 109 | } 110 | return self.listeners[event] 111 | } 112 | 113 | // ListenersCount() - return the count of listeners in the speicifed event 114 | func (self *Emitter) ListenersCount(event string) int { 115 | return len(self.Listeners(event)) 116 | } 117 | 118 | // EmitSync() - run all listeners of the specified event in synchronous mode 119 | func (self *Emitter) EmitSync(event string, args ...interface{}) *Emitter { 120 | self.mutex.Lock() 121 | defer self.mutex.Unlock() 122 | for _, v := range self.Listeners(event) { 123 | if v.once { 124 | self.removeListenerInternal(event, v.callback, true) 125 | } 126 | v.callback(args...) 127 | } 128 | 129 | return self 130 | } 131 | 132 | // EmitAsync() - run all listeners of the specified event in asynchronous mode using goroutines 133 | func (self *Emitter) EmitAsync(event string, args []interface{}) *Emitter { 134 | self.mutex.Lock() 135 | defer self.mutex.Unlock() 136 | for _, v := range self.Listeners(event) { 137 | if v.once { 138 | self.removeListenerInternal(event, v.callback, true) 139 | } 140 | go v.callback(args...) 141 | } 142 | return self 143 | } 144 | -------------------------------------------------------------------------------- /goemitter_test.go: -------------------------------------------------------------------------------- 1 | package Emitter 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestRemoveListener(t *testing.T) { 9 | emitter := Construct() 10 | 11 | counter := 0 12 | fn1 := func(args ...interface{}) { 13 | counter++ 14 | } 15 | fn2 := func(args ...interface{}) { 16 | counter++ 17 | } 18 | 19 | emitter.On("testevent", fn1) 20 | emitter.On("testevent", fn2) 21 | 22 | emitter.RemoveListener("testevent", fn1) 23 | emitter.EmitSync("testevent") 24 | 25 | listenersCount := emitter.ListenersCount("testevent") 26 | 27 | expect(t, 1, listenersCount) 28 | expect(t, 1, counter) 29 | } 30 | 31 | func TestOnce(t *testing.T) { 32 | emitter := Construct() 33 | 34 | counter := 0 35 | fn := func(args ...interface{}) { 36 | counter++ 37 | } 38 | 39 | emitter.Once("testevent", fn) 40 | 41 | emitter.EmitSync("testevent") 42 | emitter.EmitSync("testevent") 43 | 44 | expect(t, 1, counter) 45 | } 46 | 47 | func expect(t *testing.T, a interface{}, b interface{}) { 48 | if a != b { 49 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 50 | } 51 | } 52 | --------------------------------------------------------------------------------