2016-09-02 11 views
5

Piszę aplikację elektronową iw tej aplikacji potrzebuję interakcji z niektórymi interfejsami API Node.js - czytam pliki, pobieraję pozycje katalogu, słucham zdarzeń.Konwersja Idiomatic interfejsów API node.js do ClojureScript

Oczywiście, mogę napisać ClojureScript w ten sam sposób, w jaki napisałem JavaScript, ale chcę wiedzieć, co to jest ClojureScripts, wziąć API w stylu wywołania zwrotnego, strumienie, EventEmitters i jak pisać wrappery wokół API node.js w pewien sposób to nie wygląda obco w ClojureScript.

Mówiąc konkretnie:

  1. Jak napisać API, która otacza oddzwonienia stylu node.js API. (powiedzmy: fs.readdir)
  2. Jak mogę korzystać z interfejsów API podobnych do EventEmitter?
  3. (Prawdopodobnie blisko p.2) Jak pracować z API strumieni node.js?

Odpowiedz

6

Z mojego doświadczenia wynika, że ​​najprostszym sposobem rozwiązania tych problemów jest użycie core.async.

oddzwaniania Style

Na przykład czytając katalog:

(def fs (js/require "fs")) 

(defn read-dir [path] 
    (let [out (async/chan)] 
    (.readdir fs path 
     (fn [err files] 
     (async/put! (if err err files)) 
     (async/close! out))) 
    out)) 

mijam wynik w na kanał, nawet jeśli wynik jest błąd. W ten sposób wywołujący może obsłużyć błąd wykonując. Np .:

(let [res (<! (read-dir "."))] 
    (if (instance? js/Error res) 
    (throw res) 
    (do-something res)) 

Własnymi projektów używam cljs-asynchronize, który pozwala na konwersję funkcji NodeJS stylu zwrotna w celu core.async funkcji kompatybilnych. Na przykład, to jest taki sam jak w pierwszym przykładzie:

(defn read-dir [path] 
    (asynchronize 
    (.readdir fs path ...))) 

Wreszcie o ładniejszy sposób obsługi błędów przez kanały, ja osobiście uważam "Error Handling with Clojure Async" całkiem użyteczne. Tak, można napisać kod obsługi błędów powyżej jak:

(try 
    (let [res (<? (read-dir "."))] 
    (do-something res)) 
    (catch js/Error e 
    (handle-error e)) 

Strumienie

Stream API są jeszcze prostsze:

(defn create-read-stream [path] 
    (let [out (async/chan) 
     stream (.createReadStream fs path)] 
    (.on stream "close" #(async/close! out)) 
    (.on stream "data" #(async/put! out %)) 
    out)) 
+1

Czy przykład try/catch działa z pierwszym fragmentem bez zmian? –

+1

Tak. Zasadniczo

1

Myślę, że odpowiedź na to pytanie brzmi „To zależy . ".

Zawsze rozważ wszystkie opcje. To, że core.async ma sens w jednym ustawieniu, nie oznacza, że ​​jest to najlepsza opcja wszędzie.

Dobrze jest używać normalnego oddzwaniania, jeśli jest on wywoływany tylko raz i nie wymaga koordynacji z niczym innym (np. fs.readdir).

core.async świetnie nadaje się do streamowania, takich jak rzeczy, w których pojawia się pewien łańcuch zdarzeń, który chcesz zgromadzić w wyniku końcowym (np. "data","data","data","close","end").

Obietnice to kolejna opcja.

Im większa koordynacja, której potrzebujesz, tym więcej sensu. core.async marki. Jeśli nie potrzebujesz, rozważ alternatywę.

Powiązane problemy