Lightweight event management, dispatch tool library implemented by Go
Tags: programming, opensource, github, go
Published: 2022-07-07 14:37:14

`event` - Lightweight event management, dispatch tool library implemented by Go

- Support for custom definition event objects
- Support for adding multiple listeners to an event
- Supports setting the priority of the event listener. The higher the priority, the higher the trigger.
- Support for a set of event listeners based on the event name prefix `PREFIX.*`.
  - add `app.*` event listen, trigger `app.run` `app.end`, Both will trigger the `app.*` event at the same time
- Support for using the wildcard `*` to listen for triggers for all events
- Complete unit testing, unit coverage `> 95%`

## Repo and doc

- Github: https://github.com/gookit/event
- [Godoc for github](https://pkg.go.dev/github.com/gookit/event)


### Install

```shell
go install github.com/gookit/event
```

## Quick start

```go
package main

import (
	"fmt"
	
	"github.com/gookit/event"
)

func main() {
	// Register event listener
	event.On("evt1", event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	}), event.Normal)

	// Register multiple listeners
	event.On("evt1", event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	}), event.High)

	// ... ...

	// Trigger event
	// Note: The second listener has a higher priority, so it will be executed first.
	event.MustFire("evt1", event.M{"arg0": "val0", "arg1": "val1"})
}
```

> Note: The second listener has a higher priority, so it will be executed first.

### Using the wildcard

Register event listener and name end with wildcard `*`:

```go
func main() {
	dbListener1 := event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	})

	event.On("app.db.*", dbListener1, event.Normal)
}
```

Trigger events on other logic:

```go
func doCreate() {
	// do something ...
	// Trigger event
	event.MustFire("app.db.create", event.M{"arg0": "val0", "arg1": "val1"})
}

func doUpdate() {
	// do something ...
	// Trigger event
	event.MustFire("app.db.update", event.M{"arg0": "val0"})
}
```

Like the above, triggering the `app.db.create` `app.db.update` event
will trigger the execution of the `dbListener1` listener.

## Write event listeners

### Using anonymous functions

You can use anonymous function for quick write an event lister.

```go
package mypgk

import (
	"fmt"

	"github.com/gookit/event"
)

var fnHandler = func(e event.Event) error {
	fmt.Printf("handle event: %s\n", e.Name())
	return nil
}

func Run() {
	// register
	event.On("evt1", event.ListenerFunc(fnHandler), event.High)
}
```

### Using the structure method

You can use struct write an event lister, and it should implementation interface `event.Listener`.

**interface:**

```go
// Listener interface
type Listener interface {
	Handle(e Event) error
}
```

**example:**

> Implementation interface `event.Listener`

```go
package mypgk

import "github.com/gookit/event"

type MyListener struct {
	// userData string
}

func (l *MyListener) Handle(e event.Event) error {
	e.Set("result", "OK")
	return nil
}
```

## Register multiple event listeners

Can implementation interface `event.Subscriber` for register
multiple event listeners at once.

**interface:**

```go
// Subscriber event subscriber interface.
// you can register multi event listeners in a struct func.
type Subscriber interface {
	// SubscribedEvents register event listeners
	// key: is event name
	// value: can be Listener or ListenerItem interface
	SubscribedEvents() map[string]interface{}
}
```

**example**

> Implementation interface `event.Subscriber`

```go
package mypgk

import (
	"fmt"

	"github.com/gookit/event"
)

type MySubscriber struct {
	// ooo
}

func (s *MySubscriber) SubscribedEvents() map[string]interface{} {
	return map[string]interface{}{
		"e1": event.ListenerFunc(s.e1Handler),
		"e2": event.ListenerItem{
			Priority: event.AboveNormal,
			Listener: event.ListenerFunc(func(e Event) error {
				return fmt.Errorf("an error")
			}),
		},
		"e3": &MyListener{},
	}
}

func (s *MySubscriber) e1Handler(e event.Event) error {
	e.Set("e1-key", "val1")
	return nil
}
```

## Write custom events

If you want to customize the event object or define some fixed event information in advance,
you can implement the `event.Event` interface.

**interface:**

```go
// Event interface
type Event interface {
	Name() string
	// Target() interface{}
	Get(key string) interface{}
	Add(key string, val interface{})
	Set(key string, val interface{})
	Data() map[string]interface{}
	SetData(M) Event
	Abort(bool)
	IsAborted() bool
}
```

**examples:**

```go
package mypgk

import "github.com/gookit/event"

type MyEvent struct {
	event.BasicEvent
	customData string
}

func (e *MyEvent) CustomData() string {
	return e.customData
}
```

Usage:

```go
e := &MyEvent{customData: "hello"}
e.SetName("e1")
event.AddEvent(e)

// add listener
event.On("e1", event.ListenerFunc(func(e event.Event) error {
	fmt.Printf("custom Data: %s\n", e.(*MyEvent).CustomData())
	return nil
}))

// trigger
event.Fire("e1", nil)
// OR
// event.FireEvent(e)
```

## Main method

- `On/Listen(name string, listener Listener, priority ...int)` Register event listener
- `Subscribe/AddSubscriber(sbr Subscriber)`  Subscribe to support registration of multiple event listeners
- `Trigger/Fire(name string, params M) (error, Event)` Trigger event by name and params
- `MustTrigger/MustFire(name string, params M) Event`   Trigger event, there will be panic if there is an error
- `FireEvent(e Event) (err error)`    Trigger an event based on a given event instance
- `FireBatch(es ...interface{}) (ers []error)` Trigger multiple events at once
- `AsyncFire(e Event)`   Async fire event by 'go' keywords