Asynchronous ClojureScript文件预加载程序-模拟promise的函数或模式?
我正在尝试在ClojureScript中创建一个文件预加载程序。我的想法是这样一种模式:Asynchronous ClojureScript文件预加载程序-模拟promise的函数或模式?,asynchronous,clojure,promise,clojurescript,Asynchronous,Clojure,Promise,Clojurescript,我正在尝试在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)))
(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中没有承诺,所以我如何解决这个问题?基于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)))
(def url(atom[]))
(def加载的资源(atom[]))
(国防部资源陈(陈))
(defn加载回调[数据]
(转到(>!资源更改数据)))
;; 从主url填充url数组
(加载![]
;init ajax加载
(转到循环[]
(未加载时(=(在加载资源时计数)(在URL时计数))
(交换!已加载的资源)(
不确定哪个版本更好。我可以想出两种方法
将all resources
更改为另一个atom,初始化为nil。以2x-5x/秒的速度轮询它,直到它不是nil并得到“已交付”的结果
使用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"}
(定义a(原子{}))
(添加watch a:watcher)
(fn[键原子旧态新态]
(prn--“原子变化--”)
(prn“键”键)
(prn“原子”原子)
(prn“旧州”旧州)
(prn“新州”新州)
(重置!a{:foo“bar})
“原子改变了”
“钥匙”:观察者
“原子”#
;“旧州”{}
;“新州”{:foo“bar”}
{:foo“bar”}
假设加载资源函数返回一个通道(如cljs http/get)
在clj中,你所需要做的就是抓住他们做一个“等等”
(让[cs(doall(映射加载资源URL));;启动get
其他初始化
资源(地图)
在cljs中,您可以在继续之前累积响应:
(go
(let [res (atom [])]
(doseq [c cs]
(swap! res conj (<! c)))
(do-other-things @res)))
(开始
(让[res(atom[])]
(doseq[c cs]
(swap!res conj(JavaScript是一个单线程环境,因此没有阻塞等待
如果您希望请求多个资源并在它们都已被提供时继续,我建议使用core.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)))
使用goog.Promise
然后将它们放入goog.Promise.all
有趣的建议,因为google框架无论如何都会被导入…我还没有尝试过,但当时我坚持@alan thompson给我的想法,并使用了一个watcher,在一切完成后执行一个加载后钩子。它非常适合我的需要。它这也是一个坏主意,因为您处理的是可变状态,并且依赖于您的逻辑是无bug的。这正是goog.Promise。所有的都是为之而设计的。即使是javascript程序员也会理解这些代码。这是前端开发中非常常见的模式!也不要忘记错误处理,如果其中一个URL失败会发生什么?您是否等待永远?你的错误传播正确吗?保证。所有的
都能正确处理。感谢你的想法,@Alan第一种方法与比较交付资源的数量和请求资源的数量没有什么不同,只是第三个原子存储了结果,还是我错了?我也想到了使用观察者,但他们不会t阻塞主线程。当然,我可以初始化加载,添加一个观察程序,然后完成main方法。然后观察程序将检查完整性,并在加载后调用包含主线程其余部分的函数…cljs中没有
(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)))