Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throwing exception when accessing unstarted state in cljs #95

Open
fmnasution opened this issue Apr 1, 2018 · 5 comments
Open

Throwing exception when accessing unstarted state in cljs #95

fmnasution opened this issue Apr 1, 2018 · 5 comments

Comments

@fmnasution
Copy link

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.

@tolitius
Copy link
Owner

tolitius commented Apr 1, 2018

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?

@fmnasution
Copy link
Author

fmnasution commented Apr 9, 2018

Hi, sorry for the late reply!

Steps to reproduce:

  1. Clone https://github.com/fmnasution/lotus
  2. Start repl
  3. Call (start!)
  4. Open 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.

@tolitius
Copy link
Owner

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?

@tolitius
Copy link
Owner

tolitius commented Apr 17, 2018

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:

  • how they are used
  • what you expect to happen
  • how to make this expectation fail

since this is not a simple app and it would take time to debug and understand why/how and what it does.

@fmnasution
Copy link
Author

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.

tolitius added a commit that referenced this issue Oct 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants