Lifecycle, Batch, and Scheduling
Purpose
This page explains how Nrgy.js handles cleanup, coordinated updates, and reaction timing.
Scope
A Scope owns disposable resources such as effects, child scopes, atoms, and custom teardown callbacks.
import { createScope } from '@nrgyjs/core';
const scope = createScope();
const count = scope.atom(0);
scope.effect(count, (value) => {
console.log(value);
});
scope.destroy();Why scopes matter:
- they make lifetime explicit
- they group reactive resources
- they reduce leaked subscriptions
- they fit naturally into controller lifecycles
What a scope can collect:
- subscriptions returned by
effect()orsyncEffect() - atoms created through
scope.atom() - child scopes created through
scope.createScope() - arbitrary destroyable or unsubscribable resources passed through
scope.add() - custom cleanup callbacks registered through
onDestroy
When a scope is destroyed, its owned resources are destroyed or unsubscribed in one place. This is the main mechanism for feature-level cleanup.
Destroy and Cleanup
Cleanup is a first-class concern in Nrgy.js. Here cleanup means releasing resources: subscriptions, timers, sockets, long-lived state, and other objects that should not stay alive after the owning feature is gone.
Recommended practices:
- destroy controllers when the owning feature ends
- destroy atoms that hold large data and outlive local references
- register external subscriptions and sockets in scope cleanup
- use
onDestroyfor resource release, not for core business logic
Cleanup is especially important when:
- the atom holds large objects, caches, or binary data
- a feature creates timers, sockets, or external subscriptions
- a controller outlives one render but not the whole application
- state is shared broadly and can remain reachable after a screen is gone
Memory cleanup is the developer's responsibility when long-lived objects are involved.
Batch
batch() pauses effect execution until a group of updates is complete.
import { atom, batch } from '@nrgyjs/core';
const firstName = atom('Ada');
const lastName = atom('Lovelace');
batch(() => {
firstName.set('Grace');
lastName.set('Hopper');
});Use batch() when:
- several fields form one logical state transition
- observers should only see the final consistent state
- multiple writes would otherwise trigger redundant reactions
Scheduling
This is a key design choice: effects are not all treated as immediate synchronous callbacks.
Practical implications:
- Nrgy.js conceptually works with two queues: synchronous reactions and deferred reactions
- normal
effect()is typically deferred syncEffect()is available for synchronous observation- batching works naturally with deferred reactions
- reaction timing is part of the API contract and must be understood
- deferred effects usually run after the current synchronous work, which maps to microtask-like behavior from the user's point of view
This model helps avoid inconsistent intermediate states and reduces the need for manual coordination.
Why effects may not run immediately:
- several state writes can happen in one synchronous flow
- deferred execution lets the runtime observe the final stable values
- this reduces accidental work on intermediate states
- it lowers the need for manual batching in common cases
Constraints and Invariants
Keep these rules in mind:
compute()must stay pure- state writes belong in actions, effects, or explicit mutation points
- side effects belong in
effect()or controller actions, not in pure derivations - cleanup ownership should be visible from the code that creates resources
syncEffect()is an opt-in tool, not the default reaction model
Typical mistakes:
- writing to atoms from inside
compute() - hiding cleanup behind implicit global ownership
- keeping a large state alive after the owning feature is gone
- mixing derivation, mutation, and I/O in one reactive expression
Checklist
- Put cleanup strategy next to creation.
- Prefer normal
effect()unless you need strict sync semantics. - Use
batch()for multi-field updates. - Keep derived logic pure and move workflows into controllers.