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, это сделано, и если он блокирует, то у вас есть желаемая операция ожидания. Если вы хотите периодически что-то делать во время ожидания, то читайте как с этого канала, так и по таймауту.

Другие вопросы по тегам