onCleanup abstract method

void onCleanup(
  1. void cleanup()
)

Registers a synchronous cleanup callback to be executed upon turn cancellation or deactivation.

Multi-Registration & Locality of Reference (RAII)

You can call onCleanup multiple times within a single effect turn. Registering cleanups immediately adjacent to resource acquisition guarantees exception safety and modular decomposition:

Effect.scoped([room.bloom], (scope) {
  // Resource 1: acquired and immediately guarded
  final timer = Timer.periodic(const Duration(seconds: 1), (_) => ping());
  scope.onCleanup(timer.cancel);

  // Resource 2: acquired and immediately guarded
  final sub = eventStream.listen(onEvent);
  scope.onCleanup(sub.cancel);
});

Architectural Rationales

  1. Exception Safety during Setup: If an exception is thrown while acquiring a subsequent resource, previously registered cleanups remain intact and execute reliably.
  2. Modular Composition: Enables passing EffectScope into independent helper functions allowing each sub-routine to safely register its own cleanup without overwriting others.
  3. Clean Conditional Branching: Resources acquired inside if blocks can register their respective teardowns directly in-place without nullable tracking variables.

Execution Timing

Cleanups are NOT executed immediately when the action function returns. They remain dormant and active while the current turn is valid, and are executed:

  • Right before the next execution turn begins (upon upstream mutation).

  • When the effect is deactivated or unmounted (upon scope exit / disposal).

  • cleanup: The synchronous callback to execute when this turn is cancelled or deactivated.

Ensures:

  • If this scope is already cancelled, executes cleanup immediately.
  • Otherwise, chains cleanup into the pending teardown pipeline.

Implementation

void onCleanup(void Function() cleanup);