Status: done Completed: 2026-02-08 Priority: P1 Created: 2026-02-08 Owner: conductor
Application routing with statecharts requires async operations during state transitions. When a URL change triggers a route, nested routes may need to initialize parent states (load data, authenticate). These operations are async and may fail/reject. JavaScript is single-threaded — you cannot block.
The current W3C algorithm (v20150901_impl.cljc) is fully synchronous: process-event! runs microstep! in a tight loop with volatiles. Expressions execute synchronously via run-expression!. This makes async route initialization impossible within the statechart algorithm itself.
The solution is a parallel async implementation of the W3C algorithm using promesa. Expressions can return promises, and the algorithm parks when it encounters one. Existing sync expressions work unchanged with near-zero overhead — the algorithm checks return values and only parks when a promise is actually returned.
v20150901_impl.cljc that supports async expressions via promesa(fn [env data] ...) expressions must work unchanged (backwards compatible)priceless-babbage)src/main/com/fulcrologic/statecharts/algorithms/v20150901_async_impl.cljc — Core async algorithm (~900 lines)src/main/com/fulcrologic/statecharts/algorithms/v20150901_async.cljc — Public API (AsyncProcessor)src/main/com/fulcrologic/statecharts/execution_model/lambda_async.cljc — Promise-aware execution modelsrc/main/com/fulcrologic/statecharts/event_queue/async_event_loop.cljc — core.async + promise bridgesrc/main/com/fulcrologic/statecharts/event_queue/async_event_processing.cljc — Async event handlersrc/main/com/fulcrologic/statecharts/simple_async.cljc — Convenience env setupsrc/main/com/fulcrologic/statecharts/testing_async.cljc — Async test utilitiesdeps.edn — Added :async alias with promesa dependencysrc/main/com/fulcrologic/statecharts/integration/fulcro.cljc — (pending) Default to async processorsrc/main/com/fulcrologic/statecharts/integration/fulcro_impl.cljc — (pending) Handle promise resultssrc/test/com/fulcrologic/statecharts/algorithms/v20150901_async/regression_spec.cljc — Sync expressions on async processorsrc/test/com/fulcrologic/statecharts/algorithms/v20150901_async/async_spec.cljc — Promise-returning expressionsmaybe-thenThe key abstraction that enables "sometimes-async" behavior:
(defn- maybe-then [v f]
(if (p/promise? v)
(p/then v f)
(f v)))
Every place the sync algorithm calls run-expression!, the async algorithm wraps the result with maybe-then. If the result is a plain value (the common case), f is called directly — no promise overhead. If it's a promise, the chain becomes async from that point.
Copied v20150901_impl.cljc and converted all control flow:
while loops → recursive functions with maybe-thendoseq → do-sequence helper (sequential async iteration)select-transitions* → decomposed into 3 helper functionscondition-match → returns boolean or promise-of-booleanprocess-event! / initialize! → return working memory or promise-of-working-memorylambda_async.cljc — Execution model that chains data model ops via p/then when result is a promiseasync_event_loop.cljc — go-loop that bridges promises into channelsasync_event_processing.cljc — Event handler that awaits async process-event!simple_async.cljc — Wires async processor + execution modeltesting_async.cljc — Test utilities that deref promises on CLJAsyncMockExecutionModel handles promise results from expressions via p/theninstall-fulcro-statecharts! defaults to async processorerror.execution event — tested in async_spec:async alias)AsyncMockExecutionModel — the sync mock didn't handle promise results from expressions. Created async-aware mock that chains data model updates via p/then. Fixed async test specs that incorrectly passed {:run-unmocked? true} as mocks instead of mocking-options. Fixed final state regression test — W3C spec clears configuration on interpreter exit. All tests green (33 async tests, 99 assertions).Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |