Транзакции и время
Каждое обновление в Continuum происходит внутри транзакции — одного атомарного момента логического времени. e.fire(x) открывает момент; всё, что следует из x, устаканивается внутри него; момент закрывается; только после этого эффекты (listen, патчи DOM) видят результат.
Отсутствие глитчей
Если одно событие питает две ветки, которые позже сходятся («ромб»), точка слияния пересчитывается один раз за момент, видя обе ветки обновлёнными:
const doubled = count.map((n) => n * 2);
const squared = count.map((n) => n * n);
const sum = combine(doubled, squared, (d, s) => d + s);
// count: 2 → 3 переводит sum 8 → 15 одним шагом. «6 + 4» не существует.Внутри узлы ранжированы по глубине графа и обрабатываются в порядке рангов, так что узел не сработает раньше, чем устаканятся его входы. Вы этим не управляете — это и есть смысл слова «транзакция» здесь.
Одновременность — настоящая
Два вхождения в одном моменте действительно одновременны, и API заставляет сказать, что это значит:
Stream.merge(ea, eb, combine)— одновременные вхождения коалесцируются функциейcombine, а не упорядочиваются как попало;atвидит States на начало момента.
Задержка hold
hold/accum обновляют States на границе момента. Внутри транзакции, доставившей вхождение, State ещё показывает предыдущее значение:
const count = root(() => clicks.accum(0, (_e, n) => n + 1)); // модульному состоянию нужен владелец
const before = count.at(clicks);
// на 3-м клике `before` равно 2 — значение ДО этого моментаИменно это делает рекурсивные определения корректными: «новое значение зависит от старого» требует «старого», стабильного внутри момента. Прошлое доступно; настоящее ещё формируется.
Пакет из нескольких fire
Каждый fire/set обычно открывает собственный момент. Когда одно действие пользователя обновляет несколько источников, оберните их в batch — всё внутри войдёт в один момент: узлы-соединения пересчитаются один раз, merge скоалесцирует, наблюдатели сработают однажды в конце:
import { batch } from "@continuum-js/frp";
batch(() => {
query.set("");
page.set(1);
selection.set(null);
}); // один момент: один пересчёт на соединение, один патч DOM на привязкуВложенный batch присоединяется к объемлющему моменту. Нужен он редко — выведенное состояние обычно избавляет от необходимости сеттить несколько источников разом, — но когда сеттите несколько, batch не даёт промежуточным состояниям существовать вовсе.
Одно правило: у потока не больше одного вхождения за момент, поэтому двойной fire одного и того же потока внутри batch бросает ошибку (молча свернуть второе вхождение поверх до-моментного состояния — значит испортить accum). Повторный set одного state легален — побеждает последняя запись, а updates доставит подписчикам одно коалесцированное вхождение с финальным значением.
Эффекты — после
Обработчики listen выполняются в фазе post, после закрытия момента. Если обработчик порождает новое событие (редирект роутера, ответ perform), оно открывает новый момент — транзакции не вкладываются и не переплетаются.
Полный операционный разбор — три фазы, ранги, switch и соответствие книге о Sodium — в FRP-MODEL.
Незнакомый термин? Вся терминология этой документации объяснена в глоссарии.