├── 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 | ### 
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 |
--------------------------------------------------------------------------------