├── config.go ├── eventitem.go ├── concurrent.go ├── doc └── README.zh.md ├── events.go └── README.md /config.go: -------------------------------------------------------------------------------- 1 | package goevents 2 | 3 | type config struct { 4 | chNumber int //parallel channel numbers 5 | safe int //events running mod 6 | setted bool //every events can just set once. 7 | } 8 | 9 | func newConf(chm int, safe int, setted bool) *config { 10 | 11 | if chm <= 0 { 12 | chm = 5 13 | } 14 | return &config{chm, safe, setted} 15 | } 16 | 17 | //======================events object ========================= 18 | //setting the events object 19 | func (ev *events) Conf(chm int, safe int) { 20 | if ev.config.setted { 21 | return 22 | } 23 | ev.config = newConf(chm, safe, true) 24 | ev.flush() 25 | } 26 | 27 | //flush events by config 28 | func (ev *events) flush() { 29 | ev.concurrent.chNumber = ev.config.chNumber 30 | } 31 | -------------------------------------------------------------------------------- /eventitem.go: -------------------------------------------------------------------------------- 1 | package goevents 2 | 3 | import "reflect" 4 | 5 | //Event interface 6 | type EventItem interface { 7 | exec(args ...Arguments) 8 | } 9 | 10 | type Event interface { 11 | } 12 | 13 | //The struct of event 14 | type eventItem struct { 15 | fn EventFunc 16 | param []Arguments 17 | //Whether the event has run 18 | emited bool 19 | len int 20 | } 21 | 22 | //Create a new event 23 | func NewEvent(fn EventFunc, param []Arguments) *eventItem { 24 | l := reflect.TypeOf(fn).NumIn() 25 | return &eventItem{fn, param, false, l} 26 | } 27 | 28 | //Excute the current event 29 | func (this *eventItem) exec(args ...Arguments) { 30 | if this.emited { 31 | return 32 | } 33 | argvs := getArgs(args...) 34 | if len(argvs) > this.len { 35 | argvs = argvs[:this.len] 36 | } 37 | reflect.ValueOf(this.fn).Call(argvs) 38 | this.emited = true 39 | } 40 | 41 | //Convert type of ...Arguments to reflect.Value slice type 42 | func getArgs(args ...Arguments) []reflect.Value { 43 | var ma = make([]reflect.Value, len(args)) 44 | for k, v := range args { 45 | ma[k] = reflect.ValueOf(v) 46 | } 47 | return ma 48 | } 49 | 50 | //Init function 51 | func initFn() { 52 | } 53 | -------------------------------------------------------------------------------- /concurrent.go: -------------------------------------------------------------------------------- 1 | package goevents 2 | 3 | import "reflect" 4 | 5 | type Concurrent interface { 6 | gofunc(args ...Arguments) 7 | wait() 8 | end(fn EventFunc, args ...Arguments) 9 | emit() 10 | } 11 | 12 | type concurrent struct { 13 | *channelManager 14 | //Concurrent events queue 15 | loop []*eventItem 16 | //need wait gorountine 17 | waited bool 18 | endFn *eventItem 19 | currentParam []Arguments 20 | } 21 | 22 | // 23 | type channelManager struct { 24 | chNumber int 25 | ch chan int 26 | } 27 | 28 | func NewConcurrent(chNum int) *concurrent { 29 | loop := make([]*eventItem, 0) 30 | cur := make([]Arguments, 0) 31 | endFn := NewEvent(initFn, cur) 32 | return &concurrent{NewChannelManager(chNum), loop, true, endFn, cur} 33 | } 34 | 35 | func NewChannelManager(chNum int) *channelManager { 36 | ch := make(chan int, chNum) 37 | return &channelManager{chNum, ch} 38 | } 39 | 40 | //bind event to concurrent queue. 41 | func (this *concurrent) on(fn EventFunc, args ...Arguments) *concurrent { 42 | if fn == nil { 43 | return this 44 | } 45 | 46 | item := NewEvent(fn, args) 47 | 48 | this.loop = append(this.loop, item) 49 | this.currentParam = args 50 | return this 51 | } 52 | 53 | //Concurrent run the events that has been defined 54 | func (this *concurrent) gofunc(item *eventItem) { 55 | 56 | //Set argument by current param If lenght of args equal zero. 57 | if len(item.param) == 0 { 58 | item.param = this.currentParam 59 | } 60 | argvs := getArgs(item.param...) 61 | reflect.ValueOf(item.fn).Call(argvs[:item.len]) 62 | 63 | this.ch <- 1 64 | } 65 | 66 | //Wait all the goruountine finished. 67 | //If not finished will wait here. 68 | func (this *concurrent) wait() { 69 | len := len(this.loop) 70 | for i := 0; i < len; i++ { 71 | <-this.ch 72 | } 73 | } 74 | 75 | //Add the last event function called. 76 | func (this *concurrent) end(fn EventFunc, args ...Arguments) { 77 | if fn == nil { 78 | return 79 | } 80 | 81 | this.endFn.fn = fn 82 | this.endFn.param = args 83 | } 84 | 85 | //Emit all concurrent events 86 | func (this *concurrent) emit() error { 87 | 88 | if len(this.loop) == 0 { 89 | return nil 90 | } 91 | 92 | //invoke the events that was in the queue 93 | for _, e := range this.loop { 94 | go this.gofunc(e) 95 | } 96 | 97 | if this.waited { 98 | this.wait() 99 | } 100 | 101 | //p(this.chNumber) 102 | 103 | //running the last event 104 | params := this.endFn.param 105 | 106 | fn, ok := this.endFn.fn.(eventFunc) 107 | if !ok { 108 | return nil 109 | } 110 | if len(params) == 0 { 111 | fn() 112 | } else { 113 | fn(params...) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /doc/README.zh.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | 事件package。 4 | 我们可以自由灵活的使用事件,支持串行事件,并行事件; 5 | 且可以将串行事件 按定义的模块去触发. 6 | 事件函数的自定义参数无限制,但没有返回值。具体执行事件灵活自定义,用On 类函数接口注入到goevents。 7 | 可以灵活的绑定事件队列的参数。针对事件去绑定,或者,统一触发事件之前去绑定,或者混合着使用。单个绑定的优先级会更高一些 8 | 9 | ### API 10 | ###  ![English Doc](https://github.com/slclub/goevents) 11 | 12 | # 获取代码到本地 13 | 14 | go get github.com/slclub/goevents 15 | 16 | # 使用案例 17 | 18 | ### 串行化事件使用案例 19 | 20 | import "github.com/slclub/goevents" 21 | 22 | ev1 := func(args ...interface{}){ 23 | //ev1 do something. 24 | } 25 | ev2 := func(args ...interface{}){ 26 | //ev2 do something. 27 | } 28 | 29 | func main() { 30 | events := goevents.Classic() 31 | //连贯写法 绑定事件及参数 32 | events.Bind("123", "dfd").On("a", ev1) 33 | 34 | //绑定事件和参数分开写 35 | events.Bind("event2 serial running") 36 | events.On("a",ev2) 37 | 38 | //Trigger 39 | //events.Bind(...)//可以在这里绑定全局参数,当有事件绑定参数,这里可以重新绑定,只是不能针对所有串行事件 40 | //按模块名 执行事件队列。无参数 执行所有串行 事件 41 | events.Trigger("a") 42 | //触发所有事件,串行,并行的全部触发 43 | //events.Emit() 44 | } 45 | 46 | ### 并行执行事件使用案例 47 | 48 | 这里我们直接写main函数即可,害是用 上面的2个事件就可以了,并行执行因为无序,所以直接简化api不做模块化等。 49 | 50 | func main() { 51 | events := goevents.Classic() 52 | 53 | //绑定并发事件 54 | events.GoOn(ev1, "123",344,"event1 param") 55 | events.GoOn(ev2, "event2 param",454, 343,999) 56 | 57 | //最终执行事件。 58 | //events.End(func(...interface{}){}, "123","End 事件 最后所有事件执行完 才会执行") 59 | //触发并发事件不能用Trigger 60 | events.Emit() 61 | } 62 | 63 | 64 | # API document 65 | 66 | ### [events.Classic()] 67 | 68 | //instance 69 | ev := events.Classic() 70 | 71 | ### .On(name string, fn func(...interface{})) 72 | 73 | 绑定事件到events对象上 74 | 75 |   ev.On("message", func(args ...interface{}){ 76 | //do some things 77 | }) 78 | 79 | ### .Bind(args ...interface{})*events 80 | 81 | - 绑定事件参数 82 | - 返回goevents对象 83 | ``` 84 |    //绑定到当前事件上 85 |    ev.Bind("abc",123,&struct1{1,2}) 86 | 87 |    //绑定事件返回的goevents对象 所以可以连贯的写法. 88 | ev.Bind(...).On("message", func(args ...interface{}){ 89 | //do something 90 | }) 91 | ``` 92 | 93 | ### .Trigger(args ...string) 94 | 95 | - 触发事件. 96 | - 可以按照第一个参数为模块 来触发此模块的事件 97 | - 不传参数 触发所有串行执行的事件 98 | 99 | ``` 100 | 101 | //Just trigger partion of the events by first argment. 102 | ev.Trigger("message") 103 | //If no params it will emit all the serial events 104 | ev.Trigger() 105 | ``` 106 | 107 | ### .GoOn(fn func(...interface{}), args ...interface{}) 108 | 109 | - 绑定事件到并行执行的事件上. 110 | ``` 111 | ev.GoOn(func(...interface{}){ 112 | //event do something 113 | }, args) 114 | ``` 115 | 116 | ### .Emit() 117 | 118 | 触发所有的事件 并行和串行的 全部会执行. 119 | 120 | ev.Emit(). 121 | 122 | ### .Conf(chNum int, safeMod int) 123 | 124 | 设置goevents对象的method 每个对象只能设置一次 goevents.Classic() 之后马上执行. 125 | 126 | 不设置也可以非必选 127 |     128 |   ev.Conf(3,0) 129 | 130 | ### .End(func(...interface{}), args ...interface{}) 131 | 132 | 最后执行的事件多 与并行执行事件结合使用 133 | 134 | more 135 | -------------------------------------------------------------------------------- /events.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This package provides an event mechanism that you can use in any project: 6 | // The event function defined Can be adapted to any custom function as an event function 7 | // Detail:[https://github.com/slclub/goevents] 8 | package goevents 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "sync" 14 | ) 15 | 16 | var print = fmt.Println 17 | 18 | type Arguments interface{} 19 | 20 | type eventFunc func(args ...Arguments) 21 | type EventFunc interface{} 22 | 23 | //Events interface who operate the events struct 24 | //On:bind event to the queue of events 25 | //Emit all the event in the queue 26 | type Events interface { 27 | On(on string, fn func(...Arguments) error) 28 | Emit() 29 | //Can run the special event defined based on the first Arguments 30 | Trigger() 31 | } 32 | 33 | //Open parallel interface 34 | //GoOn:bind event to the queue and run run in gorountine 35 | //End:bind the last event 36 | type EventsConcurrent interface { 37 | GoOn(fn EventFunc, args ...Arguments) 38 | End(fn EventFunc, args ...Arguments) 39 | } 40 | 41 | //goevents master struct 42 | type events struct { 43 | *sync.RWMutex 44 | //Liner struct event queue 45 | loop []*eventItem 46 | 47 | // Temp Arguments for the curruent event 48 | curParam []Arguments 49 | // Current event 50 | curEvent *eventItem 51 | //eventsModule is running the evnets that added by devoloper 52 | running bool 53 | //concurrent running object 54 | concurrent *concurrent 55 | config *config 56 | } 57 | 58 | //All the events instances 59 | //It is the entry 60 | func Classic() (this *events) { 61 | this = new(events) 62 | this.loop = make([]*eventItem, 0) 63 | this.running = false 64 | this.config = newConf(0, 0, false) 65 | this.concurrent = NewConcurrent(this.config.chNumber) 66 | return 67 | } 68 | 69 | //Bind event to the attribute queue of events struct 70 | func (this *events) On(name string, fn EventFunc, args ...Arguments) { 71 | if fn == nil { 72 | return 73 | } 74 | 75 | if len(name) == 0 { 76 | name = "all" 77 | } 78 | 79 | if len(args) == 0 { 80 | args = this.curParam 81 | } 82 | item := NewEvent(fn, args) 83 | this.curEvent = item 84 | this.loop = append(this.loop, item) 85 | this.curParam = make([]Arguments, 0) 86 | 87 | return 88 | } 89 | 90 | /** 91 | * Bind param to the variable curparam 92 | * Return events master struct 93 | * It would be clear the variable curParam when invoke On function. 94 | */ 95 | func (this *events) Bind(args ...Arguments) *events { 96 | this.curParam = args 97 | this.concurrent.currentParam = args 98 | 99 | last, ok := getSlicePop(this.loop) 100 | if len(last.param) == 0 && ok == nil { 101 | last.param = args 102 | } 103 | 104 | last, _ = getSlicePop(this.concurrent.loop) 105 | if len(last.param) == 0 && ok == nil { 106 | last.param = args 107 | } 108 | 109 | return this 110 | } 111 | 112 | /** 113 | * Tiigger events 114 | * If the lenght of names bigger than one It will trigger the group events that named. 115 | */ 116 | func (this *events) Trigger(names ...string) { 117 | 118 | loop := this.loop 119 | 120 | this.running = true 121 | for _, e := range loop { 122 | print(e.param) 123 | param := e.param 124 | if len(param) == 0 { 125 | param = this.curParam 126 | } 127 | e.exec(param...) 128 | } 129 | } 130 | 131 | // Trigger all the events 132 | func (this *events) Emit() { 133 | this.Trigger() 134 | this.concurrent.emit() 135 | } 136 | 137 | // Add concurrent event 138 | func (this *events) GoOn(fn EventFunc, args ...Arguments) error { 139 | this.curParam = args 140 | this.concurrent.on(fn, args...) 141 | return nil 142 | } 143 | 144 | // Add the last event 145 | func (this *events) End(fn EventFunc, args ...Arguments) { 146 | this.concurrent.end(fn, args...) 147 | } 148 | 149 | func getSlicePop(si []*eventItem) (*eventItem, error) { 150 | l := len(si) 151 | if l == 0 { 152 | return &eventItem{}, errors.New("not fonud eventItem") 153 | } 154 | return si[l-1], nil 155 | } 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Event description 4 | 5 | Event loop. 6 | 7 | Serial and parallel events supported. 8 | 9 | You can use them seperately. Also you can also mix using serial and parallel . 10 | 11 | #### [简体中文](https://github.com/slclub/goevents/blob/master/doc/README.zh.md) 12 | ####  API documents 13 | 14 | - [Classic] 15 | 16 | # Get code to your local development. 17 | 18 | go get github.com/slclub/goevents 19 | 20 | # Usage 21 | 22 | 23 | ## first import the package 24 | 25 | import github.com/slclub/goevents 26 | 27 | ## Instance 28 | 29 | events := goevents.Classic() 30 | 31 | 32 | ## Defined your self event func 33 | 34 | 35 | Defined func follow the type.It is the beanchmark,don't need to appear in your code. 36 | Define functions "func" rather than method according to golang standards 37 | 38 | There is the function of event defined by yourself. The following definitions are allowed.Maybe you can say that as long as the golang function is allowed 39 | 40 | ``` 41 | 42 | func event1(st ...interface{}) { }//allowed 43 | 44 | func event(){}//allowed. 45 | 46 | func event(n int, str string){}//allowed. 47 | 48 | ``` 49 | 50 | 51 | ## Example of serial events 52 | 53 | func main() { 54 | 55 | self := goevents.Classic() 56 | 57 | self.On("a", func() { 58 | print("serial event added:e1") 59 | }) 60 | 61 | self.On("b", func(n int) { 62 | print("serial event added:e2; args:", n) 63 | }) 64 | self.Bind(22222) 65 | 66 | self.Bind("I am string").On("b", func(str string) { 67 | print("serial event added:e3; args:", str) 68 | }) 69 | 70 | //Can trigger the events that was named belong to On function. 71 | //events.Bind("e2", "do some things by bind").Trigger("b") 72 | 73 | //Trigger all the event functions. 74 | events.Emit() 75 | } 76 | 77 | ## Example of parallel event supported 78 | 79 | func main() { 80 | 81 | self := goevents.Classic() 82 | self.GoOn(func() { 83 | print("parallel event added e1") 84 | }, "no", 1) 85 | 86 | self.GoOn(func(str string, n int) { 87 | print("parallel event added e2", str, n) 88 | }, "no", 2) 89 | 90 | self.Emit() 91 | 92 | //trigger all of the events that has not been emited. 93 | self.Emit() 94 | 95 | ## Example of complex use case 96 | 97 | package main 98 | 99 | import ( 100 | "fmt" 101 | "github.com/slclub/goevents" 102 | "time" 103 | ) 104 | 105 | var print = fmt.Println 106 | 107 | func Test() { 108 | Timer := time.Now() 109 | self := goevents.Classic() 110 | 111 | self.On("a", func() { 112 | print("serial event added:e1") 113 | }) 114 | 115 | self.On("b", func(n int) { 116 | print("serial event added:e2; args:", n) 117 | }) 118 | self.Bind(22222) 119 | 120 | self.Bind("I am string").On("b", func(str string) { 121 | print("serial event added:e3; args:", str) 122 | }) 123 | 124 | self.GoOn(func() { 125 | print("parallel event added e1") 126 | }, "no", 1) 127 | 128 | self.GoOn(func(str string, n int) { 129 | print("parallel event added e2", str, n) 130 | }, "no", 2) 131 | 132 | self.Emit() 133 | excuTimes := time.Since(Timer) 134 | print("event running time:", excuTimes) 135 | } 136 | 137 | # API document 138 | 139 | ### [events.Classic()] 140 | 141 | //instance 142 | ev := events.Classic() 143 | 144 | ### .On(name string, fn func(...interface{})) 145 | 146 | Bind event to the object of events 147 | 148 | ev.On("message", func(args ...interface{}){ 149 | //do some things 150 | }) 151 | 152 | ### .Bind(args ...interface{})*events 153 | 154 | Bind param to the current event func 155 | ``` 156 | //use mod one 157 | ev.Bind("abc",123,&struct1{1,2}) 158 | 159 |    //you can also coherent usage. 160 | ev.Bind(...).On("message", func(args ...interface{}){ 161 | //do something 162 | }) 163 | ``` 164 | 165 | ### .Trigger(args ...string) 166 | 167 | Trigger the events by the first element of the args. it will Emit all events if no argments. 168 | 169 | //Just trigger partion of the events by first argment. 170 | ev.Trigger("message") 171 | //If no params it will emit all the serial events 172 | ev.Trigger() 173 | 174 | ### .GoOn(fn func(...interface{}), args ...interface{}) 175 | 176 | Bind event that need to parallel execution. 177 | 178 | ev.GoOn(func(...interface{}){ 179 | //event do something 180 | }, args) 181 | 182 | 183 | ### .Emit() 184 | 185 | Emit all the events. 186 | Parallel events execution included. 187 | 188 | ev.Emit(). 189 | 190 | ### .Conf(chNum int, safeMod int) 191 | 192 | Setting events object running mod. 193 | Param:chNum parallel gorountine numbers. 194 | Param:safeMod events object running mod. 195 | 196 | ev.Conf(3,0) 197 | 198 | more contact with slclub 199 | --------------------------------------------------------------------------------