Effect Helpers
withPromise() bridges a promise to actor events. withTimeout() arms a
timeout that fires an event. Both check signal.aborted before emitting, so
promises resolving after state exit and timers firing after exit produce no
side effects. Both use the actor’s clock, so tests stay deterministic on
VirtualClock.
withPromise
Section titled “withPromise”withPromise(promise, signal, emit, { success: (data) => ({ type: "paymentOk", payload: data }), error: (err) => ({ type: "paymentFail", payload: err }),});- Awaits the promise
- On resolve: calls
success, emits resulting event - On reject: calls
error, emits resulting event - Checks
signal.abortedbefore each emit - Mapper return value must be
{ type, payload? }envelope
Sugar equivalent:
setup: (m) => { m.effect(submitting, { name: "chargeCard", fn: (input) => { const s = input.context.get(); withPromise(chargeCard(s.paymentInfo!.cardNumber), input.signal, input.emit, { success: (orderId) => paymentOk.create({ orderId }), error: (reason) => paymentFail.create({ reason: String(reason) }), }); }, });};withTimeout
Section titled “withTimeout”withTimeout(ms, input, () => submittingDone.create());- Schedules timeout on the actor’s clock
- On fire: checks
signal.aborted, callseventFn, emits resulting event - State exit aborts the effect signal, no emit after exit
- Warns on invalid
ms
Equivalent without sugar:
m.effect(submitting, ({ clock, signal, emit }) => { clock.setTimeout(800, () => { if (!signal.aborted) emit(submittingDone.create()); });});VirtualClock testing
Section titled “VirtualClock testing”import { checkout } from "../checkout.ts";import { VirtualClock } from "@mantaq/core";
const clock = VirtualClock();checkout.send(submitPayment.create());clock.advance(800); // triggers timeout synchronouslymatches(checkout, "success"); // trueSame clock, same inputs, same trace.