3

我正在使用 ClojureScript+Rum 构建一个简单的 Web 应用程序,在多个地方我需要使用通过 http/get 请求从服务器获取的数据来显示 UI 片段。

通常我想在数据完全加载之前显示一个微调器。但我不想立即显示它,因为它可能会感觉闪烁(服务器可以足够快地返回响应),所以理想情况下它应该显示一些延迟(比如 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 --

但是如何延迟显示微调器?我尝试通过将“混合”“超时”和“响应”通道“混合”在一起,然后检查我从结果通道获得的值。它可以工作,但代码感觉很笨拙:


(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 --

有一个更好的方法吗?

4

1 回答 1

1

就我个人而言,我会避免使用 core.async ,除非您的并发性足够复杂以至于承诺不够。标准 js 承诺不需要担心的创建和拉取通道的开销很大。

为了延迟微调器,我会使用去抖动。也许这会有所帮助:https ://www.martinklepsch.org/posts/simple-debouncing-in-clojurescript.html

否则你可以谷歌如何实现去抖动。

基本上你想这样做:

  1. 为您的请求创建一个承诺
  2. 启动去抖动任务:修改状态以指示加载
  3. 在承诺上.finally,取消任何活动的去抖动任务并修改状态以表明您不再加载
于 2019-06-03T07:10:45.673 回答