Я создаю простое веб-приложение, использующее ClojureScript + Rum, с несколькими местами, где мне нужно показывать части пользовательского интерфейса, используя данные, полученные с сервера через запросы http / get.
Часто я хочу показать счетчик до полной загрузки данных.Но я не хочу показывать это сразу, так как это может показаться мерцанием (сервер может вернуть ответ достаточно быстро), поэтому в идеале он должен показываться с некоторой задержкой (скажем, 300 мс).Так что, если браузер получит ответ быстрее, спиннер не будет отображаться.
Как это сделать идеологическим способом core.async?(см. мою попытку ниже).
Тривиально начать показ прядильщика сразу после запуска http-запроса:
(ns my.app
(:require
;; -- snip --
[cljs-http.client :as http]
[cljs.core.async :as ca]))
(defonce *state (atom {:loading false :data nil}))
(defn get-data! []
(go
(swap! *state assoc :loading true)
(let [response (<! (http/get "/api/data"))
data (:body response)]
(swap! *state assoc :loading false :data data))))
;; -- render the data or spinner if :loading --
Но как отложить показ прядильщика?Я попытался сделать это, "смешивая" каналы "timeout" и "response" вместе, а затем проверяя значение, которое я получаю из канала результатов.Это работает, но код кажется неуклюжим:
(defonce *state (atom {:loading false :data nil}))
(defn timeout-return [out ms val]
(go
(<! (ca/timeout ms))
(ca/put! out val)))
(defn http-get [out uri]
(go
(let [response (<! (http/get uri))]
(ca/put! out response)
)))
(defn get-data! []
(go
(let [t-out (ca/chan)
resp-out (ca/chan)
out (ca/chan)
mix-out (ca/mix out)
handle-timeout (fn [] (swap! *state assoc :loading true))
handle-resp (fn [r] (swap! *state assoc :loading false :data (:body r)))]
(ca/admix mix-out t-out)
(ca/admix mix-out resp-out)
(timeout-return t-out 400 :timeout)
(http-get resp-out "/api/data")
(let [r (<! out)]
(if (= :timeout r)
(do
(handle-timeout)
(handle-resp (<! out)))
(handle-resp r)))
)))
;; -- render the data or spinner if :loading --
Есть ли лучший способ сделать это?