Fork me on GitHub

Hello, I need to create a channel on which is put the initial value at a certain path in an atom, and then any changed values, in order. I thought this would do it:

(defn from-path
  "Return a channel on which is output the current value (if any) at `path` in the atom `state`,
  and then the new value whenever the value at `path` changes"
  [state path]
  (let [initial-value (get-in @state path :not-found)
        out (chan 1)]
    (when (not= initial-value :not-found)
      (put! out initial-value))
    (add-watch state
               (path->kw path)                              ;; the unique key for this watcher. Can be used to remove the watcher
               (fn [watcher-key atom old-state new-state]
                 (let [old-value (get-in old-state path :not-found)
                       new-value (get-in new-state path :not-found)]
                   (when (not= old-value new-value)
                     (put! out new-value)))))
But I’ve seen saying that add-watches “do not always guarantee order semantics,” and my tests seem to bear this out. Any help will be appreciated…

Ferdinand Beyer08:04:43

Sounds like this has more to do with watchers than core.async. Not sure if there is a more elegant solution, but it sounds like you need to do the ordering yourself. E.g. keep the current value, and only put changes on the channel if old-value equals to the last known value. You will need to keep out-of-order changes in a queue (or separate channel) and process them again. Maybe you can also rethink your design — do you really need to watch the atom?


Thanks @U031CHTGX1T Yes, it is about watchers. I thought someone here might be well-placed to have solved it. I don’t believe there’s a correct algorithm for doing the ordering myself. And yes, I am delivering my software without this feature. I will note, however, that this is doable in TypeScript, so I thought it ought to be doable in ClojureScript as well.

Ferdinand Beyer07:05:53

Well this is certainly doable in ClojureScript. This has nothing to do with the language, but the choice of implementation. Without digging into the implementation details of watchers, are you sure that the callbacks appear out of order in ClojureScript? After all, the underlying JavaScript runtime is single-threaded. If you want order guarantees, and the watcher mechanism does not offer this guarantee, you probably need to look into something else, like core.async channels.