Documentation Index
Fetch the complete documentation index at: https://docs.goakt.dev/llms.txt
Use this file to discover all available pages before exploring further.
Actors can change their message-handling logic at runtime using behaviors. A behavior is a function func(ctx *ReceiveContext) that processes messages. The default behavior is the actor’s Receive method.
Behavior type
type Behavior func(ctx *ReceiveContext)
Behaviors are stored on a stack. The top of the stack handles each incoming message.
API
| Method | Purpose |
|---|
ctx.Become(behavior) | Replace the current behavior. The current message finishes under the old behavior; subsequent messages use the new one. No stack—previous behavior is lost. |
ctx.BecomeStacked(behavior) | Push a new behavior on top. Current message still uses the existing behavior; subsequent messages use the new one until UnBecomeStacked. |
ctx.UnBecomeStacked() | Pop the top behavior. Resume the previous one. No effect if the stack is empty. |
ctx.UnBecome() | Reset to the default (initial) behavior. Clears the stack. |
Become vs BecomeStacked
- Become — Swap behavior. Use when transitioning to a new state and you do not need to return to the previous one.
- BecomeStacked — Push behavior. Use when you need to temporarily handle messages differently, then pop back (e.g., a modal dialog, a temporary protocol phase).
Example
An account actor that switches behaviors based on authentication and transfer confirmation:
package main
import (
"context"
"fmt"
"time"
"github.com/tochemey/goakt/v4/actor"
)
// Message types
type Login struct{ User string }
type Logout struct{}
type Transfer struct {
To string
Amount int64
}
type Confirm struct{}
type Cancel struct{}
type AccountActor struct {
balance int64
pendingTransfer *Transfer // stored when awaiting confirmation
}
func (a *AccountActor) PreStart(ctx *actor.Context) error { return nil }
func (a *AccountActor) PostStop(ctx *actor.Context) error { return nil }
func (a *AccountActor) Receive(ctx *actor.ReceiveContext) {
switch ctx.Message().(type) {
case *Login:
ctx.Become(a.Authenticated)
default:
ctx.Unhandled()
}
}
func (a *AccountActor) Authenticated(ctx *actor.ReceiveContext) {
switch msg := ctx.Message().(type) {
case *Logout:
ctx.UnBecome()
case *Transfer:
a.pendingTransfer = msg
ctx.BecomeStacked(a.ConfirmTransfer)
default:
ctx.Unhandled()
}
}
func (a *AccountActor) ConfirmTransfer(ctx *actor.ReceiveContext) {
switch ctx.Message().(type) {
case *Confirm:
if a.pendingTransfer != nil {
a.balance -= a.pendingTransfer.Amount
a.pendingTransfer = nil
}
ctx.UnBecomeStacked()
case *Cancel:
a.pendingTransfer = nil
ctx.UnBecomeStacked()
default:
ctx.Unhandled()
}
}
func main() {
ctx := context.Background()
system, err := actor.NewActorSystem("behaviors-demo",
actor.WithLoggingDisabled(),
actor.WithActorInitMaxRetries(3))
if err != nil {
panic(err)
}
if err := system.Start(ctx); err != nil {
panic(err)
}
defer system.Stop(ctx)
acc := &AccountActor{balance: 1000}
pid, err := system.Spawn(ctx, "account", acc)
if err != nil {
panic(err)
}
// Login -> switches to Authenticated
_ = actor.Tell(ctx, pid, &Login{User: "alice"})
time.Sleep(10 * time.Millisecond)
// Transfer -> pushes ConfirmTransfer on stack
_ = actor.Tell(ctx, pid, &Transfer{To: "bob", Amount: 50})
time.Sleep(10 * time.Millisecond)
// Confirm -> pops ConfirmTransfer, executes transfer
_ = actor.Tell(ctx, pid, &Confirm{})
time.Sleep(10 * time.Millisecond)
fmt.Println("Done. Balance:", acc.balance) // 950
}
Stashing with behaviors
When switching behaviors, you may want to defer processing of the current message. Use ctx.Stash() to buffer it, then ctx.Unstash() or ctx.UnstashAll() after changing behavior. Requires WithStashing() at spawn. See Stashing.