Skip to content

Lifecycle, Batch и Scheduling

Назначение

Эта страница объясняет, как Nrgy.js работает с cleanup, согласованными обновлениями и временем срабатывания реакций.

Scope

Scope владеет освобождаемыми ресурсами: effects, child scopes, atoms и пользовательскими teardown callbacks.

ts
import { createScope } from '@nrgyjs/core';

const scope = createScope();
const count = scope.atom(0);

scope.effect(count, (value) => {
  console.log(value);
});

scope.destroy();

Почему scope важен:

  • делает время жизни явным
  • группирует реактивные ресурсы
  • уменьшает риск утечек подписок
  • естественно встраивается в lifecycle контроллеров

Что может собирать scope:

  • subscriptions, возвращаемые effect() или syncEffect()
  • atoms, созданные через scope.atom()
  • child scopes, созданные через scope.createScope()
  • произвольные destroyable или unsubscribable ресурсы, переданные через scope.add()
  • пользовательские cleanup callbacks, зарегистрированные через onDestroy

Когда scope уничтожается, его ресурсы уничтожаются или отписываются в одном месте. Это и есть основной механизм feature-level cleanup.

Destroy и Cleanup

В Nrgy.js cleanup является частью дизайна. Здесь cleanup означает освобождение ресурсов: subscriptions, timers, sockets, long-lived state и других объектов, которые не должны оставаться живыми после завершения owning feature.

Рекомендуемые практики:

  • уничтожать контроллеры, когда завершается owning feature
  • уничтожать atoms с большими данными, если они живут дольше локальных ссылок
  • регистрировать внешние подписки, сокеты и таймеры в scope cleanup
  • использовать onDestroy для освобождения ресурсов, а не для основной бизнес-логики

Cleanup особенно важен, когда:

  • atom хранит большие объекты, кэши или бинарные данные
  • фича создаёт timers, sockets или внешние subscriptions
  • controller переживает один render, но не всё приложение
  • state широко разделяется и может оставаться достижимым после ухода экрана

Очистка памяти для long-lived объектов остаётся ответственностью разработчика.

Batch

batch() откладывает выполнение реакций, пока не завершится группа обновлений.

ts
import { atom, batch } from '@nrgyjs/core';

const firstName = atom('Ada');
const lastName = atom('Lovelace');

batch(() => {
  firstName.set('Grace');
  lastName.set('Hopper');
});

batch() нужен, когда:

  • несколько полей образуют единый логический переход состояния
  • наблюдатели должны увидеть только финальное согласованное состояние
  • серия write-операций иначе вызовет лишние реакции

Scheduling

Это один из ключевых выборов дизайна: эффекты в Nrgy.js не сводятся к немедленным синхронным колбэкам.

Практические следствия:

  • концептуально Nrgy.js работает с двумя очередями: синхронные реакции и отложенные реакции
  • обычный effect() обычно работает отложенно
  • syncEffect() доступен для синхронного наблюдения
  • batching естественно сочетается с отложенными реакциями
  • тайминг реакций является частью API-контракта
  • отложенные эффекты обычно запускаются после текущей синхронной работы, что с точки зрения пользователя похоже на microtask-поведение

Такая модель помогает избежать наблюдения промежуточных несогласованных состояний.

Почему эффекты могут вызываться не сразу:

  • в одном синхронном flow может происходить несколько state writes
  • отложенное выполнение позволяет рантайму увидеть финальные стабильные значения
  • это уменьшает риск работы по промежуточным состояниям
  • это снижает потребность в ручном batching во многих обычных кейсах

Ограничения и инварианты

Нужно держать в голове такие правила:

  • compute() должен оставаться чистым
  • записи в state должны жить в actions, effects или явных mutation points
  • side effects должны жить в effect() или actions контроллера, а не в чистых derivations
  • владение cleanup должно быть видно из кода, который создаёт ресурсы
  • syncEffect() - это opt-in инструмент, а не дефолтная модель реакции

Типичные ошибки:

  • писать в atoms изнутри compute()
  • прятать cleanup за неявным глобальным владением
  • держать большой state после уничтожения owning feature
  • смешивать derivation, mutation и I/O внутри одного reactive expression

Чеклист

  • Стратегию cleanup надо описывать рядом с созданием ресурса.
  • По умолчанию стоит предпочитать обычный effect().
  • Для multi-field updates нужно использовать batch().
  • Производную логику надо держать чистой, а workflow переносить в controllers.