简体   繁体   中英

core.async reconnecting websocket

core.async noob here, but trying to learn by building an automatically reconnecting websocket. The idea is that the socket is abstracted away so that anyone using it does not need to worry about whether or not it's connected and can just put messages on a channel. If the socket is connected, the messages are read from the channel immediately and sent. If the socket is not currently connected, it repeatedly tries to reconnect and waits to send all pending messages once a connection has been established.

This is what I have so far:

(ns frontend.socket
  (:require-macros [cljs.core.async.macros :as asyncm :refer [go]])
  (:require [cljs.core.async :as async :refer [<! >! chan timeout]]))

(def endpoint "ws://localhost:8080")
(def socket (atom nil))
(def open (chan))
(def in (chan))
(def out (chan))
(def error (chan))
(def close (chan))

(defn open? []
  (= (.-readyState @socket) (.-OPEN @socket)))

(defn event>chan
  "Given a core.async channel, returns a
  function which takes an event and feeds
  it into the formerly given channel."
  [channel]
  (fn [e]
    (go (>! channel e))))

(defn connect
  "Opens a websocket, stores it in the socket
  atom, and feeds the socket's events into
  the corresponding channels."
  []
  (let [s (js/WebSocket. endpoint)]
    (reset! socket s)
    (set! s.onopen    (event>chan open))
    (set! s.onmessage (event>chan in))
    (set! s.onerror   (event>chan error))
    (set! s.onclose   (event>chan close))))

(defn init
  "This is the entry point from outside this
  namespace. Eagerly connects and then, if
  ever disconnected, attempts to reconnect
  every second. Also takes messages from
  the out channel and sends them through
  the socket."
  []
  (go
    (while true
      (connect)
      (<! close)
      (<! (timeout 1000))))
  (go
    (while true
      (let [m (<! out)]
        (when (or (open?) (<! open))
          (.send @socket m))))))

The reconnect logic seems to work fine, but I'm running into an issue trying to send messages after a socket has closed. In the last few lines, I check that before sending a message, the socket is open or wait for it to open. The problem is that if no message was sent while a socket was previously open, then closed, and then a message is sent, there is still something sitting on the open channel, and so the message will get sent regardless. So the open channel can get "stale", as it were.

I thought about taking from the open channel whenever the socket closes, but that just reverses the problem: then the open channel may miss values when I do need them, and messages are not sent upon reconnect.

What am I doing wrong here?

This was the wrong approach because channels will wait to be consumed. I needed to use pub/sub instead: https://github.com/clojure/core.async/wiki/Pub-Sub/549da1843c7bbe6009e9904eed49f91000d8ce2c

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM