Fork me on GitHub
#clojure
<
2023-09-02
>
sergey.shvets18:09:04

I'm a little confused on how to properly use mult in core.async. The problem I'm trying to solve is to have a chan where I load up events and over time there will be one or more "readers" that connect and process those events. The readers may come and go based on user interactions. I can easily construct a chan that accepts initial events and mult that pushes items to subsequent chans. The thing I can't grasp is how to properly "discard" events from original chan. It seems that if I don't read from original chan and only read from tapped ones, the original keeps all the values it ever received and a new tap gets a copy of all of them. I'd like the tapped channels to only receive items that happen after they tapped-in. Is there a proper way to do this? Is mult not the right tool for the problem?

hiredman18:09:27

Close it when you are done with it

hiredman18:09:19

Add a channel to the tap when a consumer wants messages, close the channel when that consumer is done

hiredman18:09:48

The mult consumes from the input channel

hiredman18:09:34

There is no history or keeping of old values

sergey.shvets18:09:50

I'm running this code in ClojureScript:

(let [ch (async/chan)
        mult-ch (async/mult ch)
        ch1 (async/chan )
        ch2 (async/chan )
        put-ch (fn [chan val]
                 (async/put! chan val
                             #(js/console.log (str "Put value" val))))
        take-confirm (fn [ch-name] #(js/console.log (str "Take " ch-name " ") %))]
    
    (go-loop []
      (let [val (async/<! ch1)]
        (js/console.log "Take chan 1" val)
        (recur)
        ))
    
    (put-ch ch 1)
    
    (async/tap mult-ch ch1)
    (js/console.log "Between puts")
    ;;(async/take! ch1 (take-confirm "ch1"))
    (put-ch ch 2)
    (async/tap mult-ch ch2)
    ;;(async/take! ch1 (take-confirm "ch1"))
    (async/take! ch2 (take-confirm "ch2"))
    (async/take! ch2 (take-confirm "ch2"))
    (js/console.log "after all takes")
    )
Output:
Between puts
after all takes
Take chan 1 1
Put value1
Take ch2  1
Put value2
Take chan 1 2
Take ch2  2
As you can see chan2 is tapped after ch1 consumed 1 already, so I assume that value should be gone, but chan2 reads it once it's connected.

sergey.shvets18:09:35

Taking from chan1 with take! instead of go-loop doesn't change the picture. Chan2 still gets both values.

hiredman19:09:29

The java script event loop doesn't get returned to until after the let has run

hiredman19:09:05

So there is not really a strict happens before relationship between all those channel operations

hiredman19:09:06

The async copy from the input to the output can't run until in js main thread is idle, and that doesn't happen until you have done both taps

sergey.shvets19:09:50

Aha, yes you're right. When I ran commands one by one it works as expected. I was confused by pending takes/put when I shoved everything into one let. Thank you!