我试图在ClojureScript中创建文件预加载器。我的想法是这样的模式:
(def urls (atom[]))
(def loaded-resources (atom []))
(def all-resources (promise))
(defn loading-callback []
(if (= (count urls) (count loaded-resources))
(deliver all-resources loaded-resources)))
;; fill urls array
;; start ajax-loading with loading-callback on success
所以我的主要功能可以继续,直到它需要资源然后等待它们,这在Clojure中运行良好。
不幸的是,承诺并不存在于ClojureScript中,那么我该如何解决这个问题呢?有promesa基于core.async频道为CLJS带来了承诺,但它只允许等待单个函数执行的未来类似的承诺,这些承诺不足以满足我的需求(至少在昨天我一直在考虑的方式......)。
有任何解决此问题的建议吗?也许使用完全不同的模式?我想让代码尽可能简单,以说服我团队中的人试用CLJ / S.
修改
在艾伦的第二个想法之后:
(def urls (atom[]))
(def loaded-resources (atom []))
(defn loading-callback [data]
(swap! loaded-resources conj data))
(defn load! [post-loading-fn]
(add-watch loaded-resources :watch-loading
(fn [_ _ _ cur]
(if (= (count cur) (count @urls)) (post-loading-fn))))
;; init ajax loading
)
(defn init []
;; fill urls array
(load! main))
(main []
(do-terrific-stuff @loaded-resources))
与此同时,我曾尝试使用core.async
(def urls (atom []))
(def loaded-resources (atom []))
(def resource-chan (chan))
(defn loading-callback [data]
(go (>! resource-chan data)))
;; fill url array from main
(load! []
;; init ajax loading
(go-loop []
(when-not (= (count @loaded-resources) (count @urls))
(swap! loaded-resources conj (<! resource-chan))
(recur)))
不确定哪个版本更好。
答案 0 :(得分:1)
我可以想到两种方法。
将all-resources
更改为另一个原子,初始化为零。轮询2x-5x / sec,直到它不是零并且具有“交付”结果。
使用add-watch
注册一个回调函数,以便在更改值时执行。这取代了阻塞,直到交付价值。这里描述:http://clojuredocs.org/clojure.core/add-watch
他们展示了一个很好的例子:
(def a (atom {}))
(add-watch a :watcher
(fn [key atom old-state new-state]
(prn "-- Atom Changed --")
(prn "key" key)
(prn "atom" atom)
(prn "old-state" old-state)
(prn "new-state" new-state)))
(reset! a {:foo "bar"})
;; "-- Atom Changed --"
;; "key" :watcher
;; "atom" #<Atom@4b020acf: {:foo "bar"}>
;; "old-state" {}
;; "new-state" {:foo "bar"}
;; {:foo "bar"}
答案 1 :(得分:0)
假设您的加载资源函数返回一个通道(如cljs-http / get)。
在clj中,您需要做的就是坚持让他们做一个&#34;等待所有&#34;。
(let [cs (doall (map load-resource urls)) ;; initiate the get
... ;; other initialisation
res (map <!! cs)] ;; wait-all for the resources
(do-other-things res))
在cljs中,您可以在继续之前累积回复:
(go
(let [res (atom [])]
(doseq [c cs]
(swap! res conj (<! c)))
(do-other-things @res)))
答案 2 :(得分:0)
JavaScript是单线程环境,因此没有阻塞等待。
如果您希望请求多个资源并继续,如果它们都已提供,我建议您使用core.async,尤其是pipeline-async。它有一个旋钮来微调异步请求的并行性。以下是用于实现您想要的惯用ClojureScript代码:
(ns example.core
(:require [cljs.core.async :refer [chan take! put! pipeline-async]
:as async]))
(defn load-resources [urls on-resources]
(let [urls-ch (chan (count urls))
resources-ch (chan)]
;; Create pipeline:
(pipeline-async 10 ;; have at most 10 requests in flight at
;; the same time, finetune as desired
resources-ch
(fn [url done-ch]
;; Pseudo code:
(request-resource
url
(fn [loaded-resource]
(put! done-ch loaded-resource))))
urls-ch)
;; Eagerly aggregate result until results-ch closes, then call back:
(take! (async/into [] resources-ch) on-resources)
;; Start the party by putting all urls onto urls-ch
;; and then close it:
(async/onto-chan urls-ch urls)))