Идиоматичный способ отсрочки показа счетчика для http-запросов с использованием core.async - PullRequest
3 голосов
/ 02 июня 2019

Я создаю простое веб-приложение, использующее 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 --

Есть ли лучший способ сделать это?

1 Ответ

1 голос
/ 03 июня 2019

Лично я бы не использовал core.async, если ваш параллелизм не достаточно сложен, чтобы обещаний было недостаточно. При создании и извлечении из каналов возникают большие издержки, о которых стандартным обещаниям js не нужно беспокоиться.

Для задержки спиннера я бы использовал дебад. Возможно, это поможет: https://www.martinklepsch.org/posts/simple-debouncing-in-clojurescript.html

В противном случае вы можете просто Google, как реализовать debounce.

По сути, вы хотите сделать это:

  1. Создайте обещание для вашего запроса
  2. Запустите задачу отладки, чтобы: изменить состояние, чтобы указать загрузку
  3. в обещании .finally, отмените все активные задачи по отладке и измените состояние, чтобы указать, что вы больше не загружаете
...