TypedActor
TypedActor<T> ist der Runtime-Host, der einen Behavior<T>-Wert
nimmt und ausführt. Intern ist es eine Actor<T>-Subklasse —
gleiche Mailbox, gleicher Dispatcher, gleicher Supervisionsbaum —
aber sein onReceive delegiert an das gerade aktive Behavior.
Du konstruierst TypedActor nicht direkt. Die Spawn-Methoden
(system.spawnTyped / context.spawnTyped und
typedActor) wickeln es für dich. Aber
zu wissen, wie es funktioniert, klärt die typed-DSL-Semantik —
besonders, was “die Runtime interpretiert das Behavior” tatsächlich
bedeutet.
Wie ein Behavior zu einem Actor wird
Abschnitt betitelt „Wie ein Behavior zu einem Actor wird“import { ActorSystem, Behaviors } from 'actor-ts';
const myBehavior = Behaviors.receive<Message>((context, message) => { // ... message behandeln return Behaviors.same;});
const system = ActorSystem.create('demo');const ref = system.spawnTypedAnonymous(myBehavior);// Unter der Haube: system.spawnAnonymous(() => new TypedActor(myBehavior))Das Framework:
- Konstruiert ein
TypedActor<T>mitmyBehaviorals initialem Wert. - In
preStartresolved es das initiale Behavior — läuft durch allesetup/withTimers/withStash/supervise-Wrapper, bis es bei einem Blatt landet (einemreceiveoder einem Sentinel). - Bei jeder Nachricht ruft es den resolved Handler auf. Der Return-Wert wird zum neuen “aktuellen Behavior”.
- Wenn der Return
sameist, ändert sich nichts. Wenn es ein frisches Behavior ist, resolved das Framework das und nimmt es als das neue aktuelle an.
Die Resolution-Loop
Abschnitt betitelt „Die Resolution-Loop“Komponierte Behaviors werden Schicht für Schicht ausgepackt. Gegeben:
const b = Behaviors.supervise( Behaviors.withTimers((timers) => Behaviors.setup((context) => Behaviors.receive((context, message) => Behaviors.same)))) .onFailure(strategy);Der Resolver läuft:
supervise(...) ← Supervise-Strategie einfangen, in Kind rekursieren withTimers(...) ← Timer-Scheduler einfangen, in Factory-Return rekursieren setup(...) ← factory(context) laufen lassen, in Return rekursieren receive(...) ← Blatt — als `current` speichernJeder Wrapper hat seinen Seiteneffekt einmal (Supervisor
installieren, Timer einfangen, Setup-Factory ausführen) und
verschwindet. Die finale Form ist ein einfacher receive-Wert.
Das Framework merkt sich die Supervise-Strategie und den
Timer-Scheduler über die Lebenszeit des Actors hinweg; die
setup-Factory läuft nur beim ersten Mal und bei Restart.
Ein Wrapper-Zyklus (ein setup, das sich selbst zurückgibt, oder
ein supervise, das rekursiert) ist auf 64 Hops gebunden — danach
wirft der Resolver, was die Fehlkonfiguration laut surfacet, statt
ewig zu drehen.
Übergänge
Abschnitt betitelt „Übergänge“Behaviors.receive<Message>((context, message) => match(message) .with({ kind: 'start' }, () => runningBehavior) .with({ kind: 'stop' }, () => Behaviors.stopped) .otherwise(() => Behaviors.same));Vier Ergebnisse:
Behaviors.same→ derselbe Closure handhabt die nächste Nachricht. Die Runtime hältcurrentunverändert.- Ein anderer
Behavior<T>-Wert → die Runtime resolved ihn und tauscht ihn ein. Wrapper führen ihre Seiteneffekte wieder aus (ein frischessetupläuft, frische Timer werden eingefangen, wennwithTimerserneut eingeführt wird — wichtig für “Phase 1 hatte einen Timer, Phase 2 nicht”-Patterns). Behaviors.stopped→ die Runtime stoppt den Actor.Behaviors.unhandled→ die Nachricht geht zu Dead Letters; das aktuelle Behavior bleibt.
Lifecycle-Signale
Abschnitt betitelt „Lifecycle-Signale“Behaviors.receiveWithSignal<Message>( (context, message) => Behaviors.same, (context, signal) => match(signal) .with({ kind: 'post-stop' }, () => { context.log.info('cleaning up'); return Behaviors.same; }) .with({ kind: 'pre-restart' }, (s) => { context.log.warn(`restarting: ${s.reason}`); return Behaviors.same; }) .with({ kind: 'terminated' }, (s) => { context.log.info(`${s.ref.path} stopped`); return Behaviors.same; }) .exhaustive(),);Der Signal-Handler wird aus den postStop /
preRestart-Hooks/Terminated-Message-Delivery-Hooks des Frameworks
aufgerufen. Drei Signale:
| Kind | Ausgelöst durch |
|---|---|
post-stop | Der Actor stoppt (aus irgendeinem Grund: PoisonPill, Behaviors.stopped, Supervisor-Stop). |
pre-restart | Ein Fehler wird gleich neu gestartet. signal.reason ist der Fehler. |
terminated | Ein beobachteter Actor (context.watch(ref)) hat gestoppt. signal.ref ist seine Ref. |
Der Return-Wert funktioniert wie beim Receive-Handler — same, um
das aktuelle Behavior zu behalten, ein neues Behavior, um zu
tauschen.
Wo die Nähte sichtbar werden
Abschnitt betitelt „Wo die Nähte sichtbar werden“TypedActor ist die Brücke — und an der Naht zwischen typed und
untyped tauchen ein paar Details auf:
- Fehler in einem typed-Handler erreichen den typed-Supervisor
zuerst. Wenn der Handler in
Behaviors.supervise(...).onFailure(strategy)gewickelt ist, behandelt diese Strategie den Fehler. Wenn nicht, propagiert er an den Supervisor des Parents — wie bei untyped. context.spawn(behavior)gibt eine voll typisierteActorRef<U>zurück. Die Runtime wickelt das Kind in einen weiterenTypedActor<U>. Das ist das cast-freie Child-Spawning, das die typed-Form bietet.- Watching ist einseitig. Ein typed-Actor kann eine untyped-Ref
context.watch(ref)en oder umgekehrt. Das Terminated-Signal kommt auf der typed-Seite über dasterminated-Signal; auf der untyped-Seite über eineTerminated-Nachricht.
Initial-Behavior-Fallstricke
Abschnitt betitelt „Initial-Behavior-Fallstricke“Das Behavior, das du an system.spawnTypedAnonymous(behavior) übergibst,
wird zum initialen aktuellen. Zwei Grenzfälle:
Behaviors.sameals Initial ist sinnlos — es gibt nichts zu behalten. Die Runtime behandelt es alsBehaviors.empty(stilles No-Op), der Actor existiert also, verwirft aber alle Nachrichten. Das deutet meist auf einen Logik-Bug irgendwo hin.Behaviors.stoppedals Initial stoppt den Actor inpreStart. Die Ref des Actors wird zurückgegeben, aber er terminiert bereits. Nützlich, wenn “soll dieser Actor existieren?” durch eine externe Prüfung zur Spawn-Zeit bestimmt wird.
Wie es weitergeht
Abschnitt betitelt „Wie es weitergeht“- Behaviors — das DSL, das die
Werte produziert, die
TypedActorinterpretiert. - Spawn Typed — die drei
Helfer, die
TypedActorfür den normalen Gebrauch wickeln. - Actor (untyped) — die
Eltern-Klasse, die
TypedActorerweitert. - Supervision — wohin
die
Behaviors.supervise(...).onFailure(...)-Strategie routet.
