p2p-cljs / src / ird / p2p / async.cljs
385 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
(ns ird.p2p.async
  "The channel contract every ird.p2p function follows, plus the tools to
  work with it.

  Every asynchronous function returns a core.async promise-chan that
  delivers exactly one value: the result (a plain map, vector, string, ...)
  or an ExceptionInfo describing the failure (see ird.p2p.errors). Nothing
  is thrown out of an SDK function; errors travel as values so a `go` block
  can decide what to do with them:

    (go (let [r (<! (p2p/join app secret))]
          (if (error? r) (handle r) (use r))))

  Inside a go block `<?` takes and rethrows errors, so ordinary try/catch
  works; `go-try` is a go block that catches whatever its body throws and
  delivers it as a value. Long-lived streams (room events, transport
  frames) are ordinary channels fed through `unbounded-chan`, so a slow
  consumer never blocks the producer and nothing is dropped."
  (:require-macros [ird.p2p.async])
  (:require [cljs.core.async :as async :refer [go go-loop <! >!]]
            [ird.p2p.impl.node :as node]))

(defn error?
  "True when V is an error value (any JavaScript Error, which includes
  ExceptionInfo)."
  [v]
  (node/error? v))

(defn throw-err
  "Throw V when it is an error value, else return it. `<?` expands to this."
  [v]
  (if (error? v) (throw v) v))

(defn unbounded-chan
  "An unbounded queue in front of a channel. Returns
  {:out <channel> :put! (fn [v]) :close! (fn [])}: `put!` never blocks and
  never drops, `out` delivers the values in order, and `close!` closes
  `out` once everything queued has been taken."
  []
  (let [q (atom #queue [])
        wake (async/chan (async/sliding-buffer 1))
        out (async/chan)
        done (atom false)]
    (go-loop []
      (let [v (peek @q)]
        (cond
          (some? v) (do (swap! q pop)
                        (when (>! out v) (recur)))
          @done (async/close! out)
          :else (do (<! wake) (recur)))))
    {:out out
     :put! (fn [v] (swap! q conj v) (async/offer! wake true) nil)
     :close! (fn [] (reset! done true) (async/close! wake) nil)}))

(defn promise->chan
  "Adapt a JS Promise to the channel contract."
  [p]
  (node/promise->chan p))

(defn ->promise
  "Adapt an ird.p2p channel to a JS Promise: resolves with the delivered
  value, rejects when that value is an error — for the edge of a
  :node-library build."
  [ch]
  (node/chan->promise ch error?))

(defn sleep
  "The default :sleep-fn — a channel that closes after MS milliseconds."
  [ms]
  (async/timeout (max 0 ms)))

(defn deliver!
  "Put V on the promise-chan CH (closing it when V is nil)."
  [ch v]
  (if (nil? v) (async/close! ch) (async/put! ch v))
  nil)

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-cljs.git