Идиоматичный способ отсрочки показа счетчика для http-запросов с использованием core.async

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

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

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

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

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

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