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)
hmm. TMI?
(full disclojure: i'm a bit of a noob)
I'm using (for better or worse) this "haslett" library I found, https://github.com/weavejester/haslett
as a minimal test app, I created a small reagent app that connects to <wss://echo.websocket.org>. 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"
[id]
(:body (http/GET {...}))
(defn take-4-first-non-nils [ids] ...)
Thanks!
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]
(a/go
(let [wait (rand 5000)]
(a/<! (a/timeout wait))
(a/>! response-chan [n wait]))))
(let [results-chan (a/chan 15 (take 4))]
(a/go
(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])
Nice!
(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?
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
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