2016-11-10 6 views
1

私は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))) 

;; fill urls array 
;; start ajax-loading with loading-callback on success 

それがリソースを必要として、Clojureの中でうまく機能している、彼らのために待ってまで、だから私の主な機能は上に行くことができます。

ClojureScriptには約束が存在しないので、どうすれば問題を回避できますか? core.asyncチャンネルに基づいてCLJSに約束してくれるpromesaがありますが、私の必要性を満たしていない単一の機能が実行されるのを待つ将来のような約束しかできません(少なくとも私が昨日考えていた方法で...)。

この問題を解決するための提案はありますか?たぶん完全に違ったパターンを使うのでしょうか?私はCLJ/Sを試してみるために、私のチームの人々を説得するために、コードをできるだけシンプルに保っていきたいと思います。

EDIT:アランの二アイデア後

一方私は優れているバージョン 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 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)) 

+1

'goog.Promise'を使って' googでそれらを押してください。Promise.all' – ClojureMostly

+0

興味深い提案、とにかくGoogleのフレームワークがインポートされるように...私はまだそれを試していないが、アイデアにこだわりました@アラン - トンプソンは私を与えて、すべてが完了したらローディングフック。それは私の必要性のために非常にうまくいく。 – waechtertroll

+1

あなたが可変状態を扱い、あなたのロジックにバグがないことに依存しているので、それはまた悪い考えです。これはまさにgoog.Promise.allが作られたものです。 javascriptのコーダーでもそのコードを理解できます。それはフロントエンド開発の非常に一般的なパターンです! – ClojureMostly

答えて

1

2つのアプローチが考えられます。

  1. all-resourcesを別のアトムに変更し、ゼロで初期化します。それがnilでなく "配信された"結果になるまで、それを2x-5x /秒でポーリングします。

  2. 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" #<[email protected]: {:foo "bar"}> 
;; "old-state" {} 
;; "new-state" {:foo "bar"} 
;; {:foo "bar"} 
+0

あなたの考えをお寄せいただきありがとうございます。@Alan最初のアプローチは、3番目のアトムが結果を格納することを除いて、要求されたリソースの数と配信されるリソースの数を比較することと変わりありませんか、間違っていますか?ウォッチャーを使用することも私に起こりましたが、彼らはメインスレッドをブロックしません。私は、もちろん、読み込みを初期化し、ウォッチャーを追加してから、メインメソッドを終了することができます。ウォッチャーは完全性をチェックし、ロード後にメインスレッドの残りの部分を含む関数を呼び出します。 – waechtertroll

0

は(cljs-HTTP/GETのような)チャネルを返します。

cljでは、あなたがする必要があるのは、「待機」をすることです。 cljsで

(let [cs (doall (map load-resource urls)) ;; initiate the get 
     ...         ;; other initialisation 
     res (map <!! cs)]      ;; wait-all for the resources 
    (do-other-things res)) 

続行する前に、あなたが回答を蓄積することができます

(go 
    (let [res (atom [])] 
     (doseq [c cs] 
     (swap! res conj (<! c))) 
     (do-other-things @res))) 
+0

cljsには '

+0

指摘してくれてありがとう。回答が更新されました。 – rmcv

0

JavaScriptをブロッキング待ちがないので、シングルスレッド環境です。

複数のリソースをリクエストしてそれらがすべて提供されている場合は続行するには、core.asyncを使用することをお勧めします。特にpipeline-asyncを使用することをお勧めします。非同期要求の並列性を微調整するためのノブがあります。

(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))) 
関連する問題