Aller au contenu
Français

EventStream

Ce contenu n’est pas encore disponible dans votre langue.

Defined in: src/EventStream.ts:72

A simple system-wide pub/sub bus. Subscribers register against a channel; publications are matched against it and tell’d to everyone interested.

Two ways to name a channel. A class, matched with instanceof, so subclass instances reach base-class subscribers — which is what makes an abstract base the most useful channel there is. Or an event’s kind, named by an EventKey or by the bare string, matched on the discriminant: the form the project’s own message convention needs, since a kind-discriminated plain type has no constructor to hand over and until recently could not be subscribed to at all — even though publish has always accepted one.

Predicate-filtered subscriptions (#85). Each subscription may carry an optional predicate that runs against the event before delivery — only events the predicate accepts are tell’d to the subscriber. Useful for high-frequency channels (cluster events, metrics) where the consumer only cares about a slice of the traffic and would otherwise have to filter inside its own onReceive. A predicate that throws is treated as “no match” for that delivery; the subscription stays active.

new EventStream(): EventStream

EventStream

optional log?: EventStreamLogger

Defined in: src/EventStream.ts:81

Optional logger used to surface predicate failures. Assigned by ActorSystem once its main logger has been constructed; tests that instantiate EventStream directly can leave it undefined — the bus stays functional, errors just stay silent.

publish(event): void

Defined in: src/EventStream.ts:204

Publish an event to all matching subscribers.

The recipient set is fixed when the publish starts. subscriber.tell can run synchronously — a PromiseActorRef, a test probe, an actor on an inline dispatcher — so a handler may subscribe or unsubscribe while the event is still being delivered.

The two operations used to disagree about what that meant, because of how they happened to be written rather than by decision: unsubscribe reassigns subs, so the loop kept walking the array it started with and a removed subscriber still got the event, while subscribe pushes into that same array, so a subscriber added mid-delivery received an event published before it existed (#645).

Iterating a snapshot settles it in the direction that was already true for unsubscribe: everyone subscribed when publish was called receives the event, and nobody else. Delivering to a subscriber that did not exist at publish time is the indefensible half; delivering one last event to a subscriber on its way out is harmless — it lands in dead letters like any other message to a stopped actor.

One bad subscription cannot take the others down (#1010). Everything done on a subscription’s behalf runs under a guard, because all three steps can throw: instanceof on a channel that turned out not to be one, the predicate, and subscriber.tell. Only the predicate used to be guarded, so a single faulty entry raised a TypeError into whoever called publish — and since the throw escaped the loop, every subscription registered after it silently stopped receiving anything, in an order no caller controls. That reached far: publish runs on every actor start, every actor stop and every dead-lettered tell, so it turned ref.tell, an API that does not throw by contract, into one that did.

object

void


subscribe<TEvent>(subscriber, channel, predicate?): boolean

Defined in: src/EventStream.ts:116

Subscribe an actor ref to a channel. Returns true if a new subscription was added; false if a duplicate was rejected.

Naming the channel. A class, an EventKey, or the bare kind string. The string is the shorthand and it costs the type: TEvent has nothing to be inferred from and falls back to unknown, so a predicate written against it sees unknown unless the caller spells the argument out — subscribe<UserLoggedInEvent>(ref, 'user-logged-in'), which also makes the string itself checkable against the type’s kind.

A key and its string are the same channel: subscribing both ways dedups, and either one unsubscribes the other. A class and a kind are not, even when the class’s instances carry that kind — those are two channels selecting overlapping events, exactly like a base class and its subclass, and an actor holding both subscriptions receives both deliveries.

Dedup rules. Without predicate, only one subscription per (subscriber, channel) is kept — re-calling subscribe is a no-op. With a predicate, every call adds a new subscription: predicates are values without an identity contract, so dedup’ing across them would be unreliable; users wanting “replace this filter” should unsubscribe first.

TEvent

ActorRef

EventChannel<TEvent>

(event) => boolean

boolean

TypeError when channel is neither a usable instanceof right-hand side nor a non-empty kind. Failing on the line that wrote the subscription is the whole point: the alternative is a subscription that poisons an unrelated publish in another actor much later (#1010). It throws rather than returning false, because false already means “duplicate rejected” and conflating the two destroys the signal the return value carries.


unsubscribe<TEvent>(subscriber, channel?): boolean

Defined in: src/EventStream.ts:158

Unsubscribe a (subscriber, channel) pair, or every subscription the actor holds when channel is omitted. Removes ALL matching entries — including predicate-bearing ones; finer-grained removal (one specific predicate at a time) isn’t supported because predicates have no stable identity.

The test is !== undefined, not truthiness. Truthiness was correct while a channel could only be a constructor; with kind strings legal, '' is a supplied channel that reads as falsy, and the old shape would have taken the omitted-channel branch and dropped every subscription the actor held.

The channel is resolved exactly as subscribe resolved it, so it is named by identity rather than by object: EventKey.of('x') mints a fresh key on every call and would match nothing under ===. An invalid channel throws here too — quietly removing nothing is how a subscription survives a cleanup that believed it had done its job (#645, #763).

TEvent

ActorRef

EventChannel<TEvent>

boolean