Clojurescript + Om: дождитесь изменения состояния, затем сделайте что-нибудь
Я пытаюсь сделать приложение Clojurescript, которое показывает рецепты.
Соответствующий код следует (также доступно в виде сущности):
(defn load-recipes [data]
(go (if (not (:loaded? @data))
(let [recipes-data (<! (fetch-recipes data))]
(om/update! data :recipes recipes-data)
(om/update! data :loaded? true))
(println "Data already loaded"))))
(defn define-routes [data]
(defroute home-path "/" []
(om/update! data :view :home))
(defroute "/random" []
(go (loop [loaded? (:loaded? (om/value data))]
(if-not loaded? (do (println "Waiting for data...")
(recur (:loaded? (om/value data))))
(do (om/update! data :tag
(rand-nth
(vec (apply set/union (map :tags (:recipes @data))))))
(om/update! data :view :random)))))))
(defn app-view [data owner]
(reify
om/IWillMount
(will-mount [_]
(do
(load-recipes data)
(define-routes data)))
om/IDidMount
(did-mount [_]
#_(fetch-recipes data))
om/IRender
(render [_]
(html data))))
Чего я хочу достичь:
- Сначала получите рецепты, используя асинхронный http-вызов. Я использую cljs-http.client, который возвращает канал
- Определите маршруты, используя секретарскую библиотеку. В / случайном маршруте я хочу выбрать случайный рецепт. Это может произойти только тогда, когда данные были получены и обновлены в атоме приложения.
Теперь я получаю бесконечный цикл в браузере. Что происходит?
Другой вариант - обернуть все мои маршруты, которым нужно сначала получить данные, в блок go и поместить (<! (load-recipes))
на первой линии.
PS: я закончил с
(defn ensure-recipes-loaded [data]
(go (if (not (:loaded? (om/value data)))
(do (om/update! data :view :loading)
(let [recipes-data (<! (fetch-recipes data))]
(om/update! data :recipes recipes-data)
(om/update! data :loaded? true)))
(println "Data already loaded"))))
(defn define-routes [data]
(defroute home-path "/" []
(om/update! data :view :home))
(defroute "/random" []
(go
(<! (ensure-recipes-loaded data))
(do (om/update! data :tag
(rand-nth
(vec (apply set/union (map :tags (:recipes @data))))))
(om/update! data :view :random))))
(defroute "/random/:tagname" [tagname]
(go (<! (ensure-recipes-loaded data))
(om/update! data :tag tagname)
(om/update! data :view :random)))
(defroute "/recipe/:link" [link]
(go (<! (ensure-recipes-loaded data))
(om/update! data :view :recipe)
(om/update! data :permalink link)))
(defroute "*" [*]
(go (<! (ensure-recipes-loaded data))
(om/update! data :view :default))))
1 ответ
Используя loop
Конструкция без каких-либо операций (стоянки) внутри канала для ожидания в значительной степени противоречит духу core.async. Помните, что JavaScript является однопоточным, поэтому, если вы не паркуете поток выполнения, у вас больше не будет возможности что-либо запустить.
Есть канал, который вы закрываете, когда операция загрузки завершена. Попробуйте прочитать с этого канала, когда вам нужно заблокировать; если он вернется nil
, это сделано, и если он блокирует, то у вас есть желаемая операция ожидания. Если вы хотите периодически что-то делать во время ожидания, то читайте как с этого канала, так и по таймауту.