Open fmnasution opened 6 years ago
the idea behind starting the state at @
is to avoid null pointers and enable starting only those components that are used.
this will make the given state might start more than once and possibly in the wrong order
can you provide an example?
Hi, sorry for the late reply!
Steps to reproduce:
(start!)
localhost:8080
You will see in the console log that several states is started more than once and possibly in the wrong order. In my case it's #'lotus.router.html-router
, #'lotus.element.element
, and #'lotus.ajax.ring-ajax
.
started several times, states started in exactly the same order:
{:started
["#'lotus.command/event-dispatcher"
"#'lotus.command/effect-dispatcher"
"#'lotus.config/config"
"#'lotus.datastore/datastore-conn"
"#'lotus.ws/ws-remote"
"#'lotus.command.listener/event-listener"
"#'lotus.command.listener/effect-listener"
"#'lotus.datastore.bootstrapper/datastore-conn-bootstrapper"
"#'lotus.datastore.watcher/datastore-conn-watcher"
"#'lotus.datastore.listener/datastore-conn-listener"
"#'lotus.ws.listener/ws-listener"
"#'lotus.router/ring-middleware"
"#'lotus.router/ring-router"
"#'lotus.web/web-server"
"#'lotus.figwheel/figwheel-server"]}
you could also use mount.tools.graph
to see deps and order:
=> (require '[mount.tools.graph :as graph])
=> (graph/states-with-deps)
({:name "#'lotus.command/event-dispatcher",
:order 1,
:status #{:started},
:deps #{}}
{:name "#'lotus.command/effect-dispatcher",
:order 2,
:status #{:started},
:deps #{}}
{:name "#'lotus.config/config",
:order 3,
:status #{:started},
:deps #{}}
{:name "#'lotus.datastore/datastore-conn",
:order 4,
:status #{:started},
:deps #{"#'lotus.config/config"}}
{:name "#'lotus.ws/ws-remote",
:order 5,
:status #{:started},
:deps #{}}
{:name "#'lotus.command.listener/event-listener",
:order 6,
:status #{:started},
:deps
#{"#'lotus.ws/ws-remote" "#'lotus.datastore/datastore-conn"
"#'lotus.command/effect-dispatcher" "#'lotus.config/config"
"#'lotus.command/event-dispatcher"}}
{:name "#'lotus.command.listener/effect-listener",
:order 7,
:status #{:started},
:deps
#{"#'lotus.ws/ws-remote" "#'lotus.datastore/datastore-conn"
"#'lotus.command/effect-dispatcher" "#'lotus.config/config"
"#'lotus.command/event-dispatcher"}}
{:name "#'lotus.datastore.bootstrapper/datastore-conn-bootstrapper",
:order 8,
:status #{:started},
:deps #{"#'lotus.datastore/datastore-conn"}}
{:name "#'lotus.datastore.watcher/datastore-conn-watcher",
:order 9,
:status #{:started},
:deps
#{"#'lotus.datastore/datastore-conn"
"#'lotus.datastore.bootstrapper/datastore-conn-bootstrapper"}}
{:name "#'lotus.datastore.listener/datastore-conn-listener",
:order 10,
:status #{:started},
:deps
#{"#'lotus.command/event-dispatcher"
"#'lotus.datastore.watcher/datastore-conn-watcher"}}
{:name "#'lotus.ws.listener/ws-listener",
:order 11,
:status #{:started},
:deps #{"#'lotus.ws/ws-remote" "#'lotus.command/event-dispatcher"}}
{:name "#'lotus.router/ring-middleware",
:order 12,
:status #{:started},
:deps
#{"#'lotus.ws/ws-remote" "#'lotus.datastore/datastore-conn"
"#'lotus.config/config"}}
{:name "#'lotus.router/ring-router",
:order 13,
:status #{:started},
:deps
#{"#'lotus.ws/ws-remote" "#'lotus.datastore/datastore-conn"
"#'lotus.config/config"}}
{:name "#'lotus.web/web-server",
:order 14,
:status #{:started},
:deps #{"#'lotus.router/ring-router" "#'lotus.config/config"}}
{:name "#'lotus.figwheel/figwheel-server",
:order 15,
:status #{:started},
:deps #{"#'lotus.config/config"}})
is it not what you see?
as to cljs
, can you narrow it down to, say, two specific states that start out of order and provide a bit more details on:
since this is not a simple app and it would take time to debug and understand why/how and what it does.
Hi tolitius, thanks for the reply!
When it comes to clj
the states are working fine. They start once and in order. The problem is in cljs
where some states would start multiple times and in the wrong order. The problematic states for me is: #'lotus.router.html-router
, #'lotus.element.element
, and #'lotus.ajax.ring-ajax
.
I actually have found a workaround for this. Most of my states basically has "async" property in it, meaning it accept a callback to start and in the callback it calls another state like:
(defn start-a!
[callback-manager]
(actually-start-a! (fn [data]
(act-on-data callback-manager data))))
(defstate b
:start (start-b!))
(defstate a
:start (start-a! @b))
This becomes a problem where it is actually calling the callback right at the start (Like #'lotus.router.html-router
, it is basically a wrapper around bidi.router/start-router!
). The solution is to defer the calling of the dependency, in this case it's b
.
(defn start-a!
[callback-manager]
(actually-start-a! (fn [data]
(act-on-data @callback-manager data))))
(defstate b
:start (start-b!))
(defstate a
:start (start-a! b))
I think this is one of those "gotchas" where a state shouldn't be accessed asynchronously.
Is there anyway to do that? Right now accessing unstarted state in cljs will forcefully start the state, this will make the given state might start more than once and possibly in the wrong order.