Fork me on GitHub

So I'm trying to make a simple cljs web app (SPA) that communicates over a websocket. I want the app to basically always be connected. So, if the network goes down, or the laptop goes asleep, or the browser on the phone goes to the background, I want it such that whenever the app comes to the foreground, the websocket will be reestablished automatically. (see thread for details)


(full disclojure: i'm a bit of a noob)


I'm using (for better or worse) this "haslett" library I found,


as a minimal test app, I created a small reagent app that connects to <wss://>. I added a button to the webpage; when you click it, a counter atom is incremented and the value is put! on the :sink channel. Then I have a go-loop which sits on the :source channel and logs what it reads to the js console. So, this basically works. The problem is all of the real-world (dis)connectivity issues, for ex.,


if I shut off my wifi, the websocket closes; the go-loop reads 1 nil message from the :source channel. At this point, the haslett websocket is basically dead, done, useless.


Another ex: if I give a bad hostname to the websocket connection and try to connect, connection fails (of course) but I get no real indication of a problem except for the go-loop reads a nil from the :source. I don't get any "closed" message from the :closed-status channel. I suppose I understand the logic of not getting a closed indication from something that never successfully opened.


I guess what I'm trying to figure out is, how should i structure the entire system with connecting and reconnecting the websocket when exceptional problems occur.


Like, should I store the haslett connection object in an atom, and swap! in new connection once established? Should my main go-loop use an alt! to look for either next message or for websocket closed message? And upon finding websocket-closed, I want to enter a try-reconnect/sleep loop, in perpetuity, until reconnection happens?


And, isn't what I'm describing going to be commonly desired? Are there not libraries that already solve this? Like, it feels like a more useful abstraction for a websocket would be: "Just always be connected to X. During any time connectivity is unavailable, let me know. Any time connectivity is restored, let me know."


Should I make better use of core.async channels and try to compose channels to the "higher level of abstraction" I'm seeking?


Hi! Say I want the 4 fastest non-nils answers from 10 concurrent api calls, what is a good and fast way to do it?

(def ids `[1 2 3 4 5 6 7 8 9 10])
(defn ask 
  "may return nil"
  (:body (http/GET {...}))
(defn take-4-first-non-nils [ids] ...)


if the 10 api calls each put onto a channel can't you just take from the channel as many values as you like?


(defn random-get [response-chan n]
    (let [wait (rand 5000)]
      (a/<! (a/timeout wait))
      (a/>! response-chan [n wait]))))

(let [results-chan (a/chan 15 (take 4))]
    (dotimes [n 10]
      (random-get results-chan n)))
  (prn (repeatedly 4 #(a/<!! results-chan))))

([8 94.14180803848204] [2 123.12621124015766] [9 655.8796335112349] [0 911.5708679904172])


(a/chan 15 (comp (take 4) (remove nil?))) ?


i don't think you need to remove nils from a chan as they aren't valid values fora channel to receive right?

👍 12

this could also be a good use case for clojure.core.async/merge, borrowing from @dpsutton's example. merge returns a channel which contains the values taken from a collection of source channels, so you'll end up seeing the values on the merged channel in the order they appear on one of the sources

👍 4

the use of alts makes a subtle difference here it seems. They don't come back in order necessarily. If there are 4 channels with values it won't take the first that came back but one of those 4 channels