# Emitter

> Announce that something happened with typed events, react with listeners, run them in the background, and fake them in tests.

This guide covers events in Tinka. You will learn how to:

- Define events as Go types and emit them
- Add listeners, and remove them
- Wait for listeners, run them in order, or run them in the background
- Handle errors from listeners in one place
- Check emitted events in tests without running listeners

## Overview

Events let one part of an App announce that something happened, and other parts react, without knowing about each other. After a user signs up, the App sends a welcome email, adds the user to the CRM and records the sign-up for analytics. The sign-up code shouldn't know about any of that: it emits a `UserRegistered` event, and a listener for each concern reacts.

The `events` package (`github.com/mucolabs/tinka/events`) delivers events. An event is any Go type, usually a struct holding what listeners need. A listener is a function that receives it. Because the event's type is its name, the compiler checks that every emit and every listener agree on what the event holds.

## Create an emitter

Create one `Emitter` for the App, as a singleton in its [container](../concepts/dependency-injection.md), so the App waits for background listeners when it [shuts down](../concepts/application-lifecycle.md#the-shutdown-phase):

```go title="main.go"
	app.Container().Singleton(func(*di.Resolver) (*events.Emitter, error) {
		return events.New(), nil
	})
```

## Define and emit events

Define each event as a type. Emit it where it happens, passing the request's context:

```go title="accounts.go"
package main

import (
	"net/http"

	"github.com/mucolabs/tinka"
	"github.com/mucolabs/tinka/events"
)

type UserRegistered struct {
	User User
}

type Accounts struct {
	Events *events.Emitter
	Users  *UserStore
}

func (a Accounts) SignUp(c *tinka.Ctx) (User, error) {
	in, err := c.Body[SignUpForm]()
	if err != nil {
		return User{}, err
	}
	user, err := a.Users.Create(c, in.Email)
	if err != nil {
		return User{}, err
	}
	if err := a.Events.Emit(c, UserRegistered{User: user}); err != nil {
		return User{}, err
	}
	c.Status(http.StatusCreated)
	return user, nil
}
```

## Add listeners

`On` adds a listener. Its event parameter's type says which events it receives. Register listeners while the App [starts](../concepts/application-lifecycle.md#the-start-phase), next to your routes:

```go title="listeners.go"
func listen(bus *events.Emitter, mailer *Mailer, crm *CRM) {
	bus.On(func(ctx context.Context, e UserRegistered) error {
		return mailer.SendWelcome(ctx, e.User)
	})
	bus.On(func(ctx context.Context, e UserRegistered) error {
		return crm.AddContact(ctx, e.User.Email)
	})
}
```

A listener receives events of its exact type only: a type that embeds `UserRegistered` is another event. `Once` adds a listener for the next event only, and `OnAny` adds one for every event, such as one that logs them:

```go
	bus.OnAny(func(ctx context.Context, e any) error {
		logger.InfoContext(ctx, "event", "type", fmt.Sprintf("%T", e))
		return nil
	})
```

`On` and `OnAny` return a function that removes the listener. `Clear[T]` removes every listener of one event type, and `ClearAll` removes them all.

## Wait, run in order, or run in the background

`Emit` runs every listener at once and waits for all of them, so the handler continues once the reactions are done. It returns their errors together.

`EmitSerial` runs listeners one at a time, in the order they were added, and stops at the first error: use it when a listener depends on the one before.

`EmitAsync` returns at once and runs the listeners in the background, for work the user shouldn't wait for, such as sending an email:

```go title="accounts.go" del={1-3} ins={4}
	if err := a.Events.Emit(c, UserRegistered{User: user}); err != nil {
		return User{}, err
	}
	a.Events.EmitAsync(c, UserRegistered{User: user})
```

Background listeners keep the request's context values, such as its ID for the logs, but not its cancellation, so they finish after the response is sent. When the App shuts down, it waits for them, within its shutdown timeout.

## Handle errors

A listener that returns an error or panics doesn't stop the others: a panic becomes an error instead of crashing the App. `Emit` and `EmitSerial` return the errors. Errors from `EmitAsync` have no caller to return to, so they're logged.

`OnError` sends every listener's error to one function instead, such as one that reports to your error tracker. With it set, `Emit` and `EmitSerial` return `nil`:

```go title="listeners.go"
	bus.OnError(func(ctx context.Context, event any, err error) {
		logger.ErrorContext(ctx, "event listener failed", "event", fmt.Sprintf("%T", event), "err", err)
	})
```

## Fake events in tests

To test code that emits events without running their listeners, such as ones that send real email, call `Fake`. Events are recorded instead of delivered, and the returned `Recorder` checks them. Restore the emitter when the test ends:

```go title="accounts_test.go"
func TestSignUp(t *testing.T) {
	bus := events.New()
	rec := bus.Fake()
	t.Cleanup(bus.Restore)

	accounts := Accounts{Events: bus, Users: newUserStore(t)}
	signUp(t, accounts, "ada@example.com")

	rec.AssertEmitted(t, func(e UserRegistered) bool {
		return e.User.Email == "ada@example.com"
	})
	rec.AssertNotEmitted[OrderShipped](t)
}
```

Pass event types to fake some only: `bus.Fake(events.TypeOf[UserRegistered]())` records sign-ups while other events reach their listeners.

::::options

:::option{name="AssertEmitted[T]" type="func(t TB, match ...func(T) bool)"}
Fails unless an event of type `T`, matching `match` when given, was emitted.
:::

:::option{name="AssertNotEmitted[T]" type="func(t TB, match ...func(T) bool)"}
Fails if an event of type `T`, matching `match` when given, was emitted.
:::

:::option{name="AssertEmittedCount[T]" type="func(t TB, n int)"}
Fails unless exactly `n` events of type `T` were emitted.
:::

:::option{name="AssertNoneEmitted" type="func(t TB)"}
Fails if any event was emitted.
:::

:::option{name="Emitted[T], All, Len, Flush" type="Recorder methods"}
Return the recorded events of type `T`, all of them, or how many; `Flush` forgets them.
:::

::::

See also: [Application lifecycle](../concepts/application-lifecycle.md), [Dependency injection](../concepts/dependency-injection.md), [Logger](logger.md)
