re-frame

p-himik 2024-12-02T12:08:11.251929Z

Oh wow, a non-CLJS usage of re-frame in the wild that's not for tests! First time I see something like that. :)

Kimo 2024-12-02T12:18:01.800259Z

My takeaways so far: • calling dispatch actually pushes an event (vector) onto a https://github.com/day8/re-frame/blob/f654f42430ac0bad9ad43688ebc9d712fb26b4e6/src/re_frame/router.cljc#L222 • when re-frame pulls an event from the queue (to call the event handler), that's called a https://github.com/day8/re-frame/blob/f654f42430ac0bad9ad43688ebc9d712fb26b4e6/src/re_frame/router.cljc#L139 • this queue https://github.com/day8/re-frame/blob/f654f42430ac0bad9ad43688ebc9d712fb26b4e6/src/re_frame/router.cljc#L31, leaving time for stuff like draw calls & (non-re-frame) event loops • when running re-frame on the JVM, ticks are https://github.com/day8/re-frame/blob/f654f42430ac0bad9ad43688ebc9d712fb26b4e6/src/re_frame/interop.clj#L31. • the executor needs an explicit shutdown call. Otherwise, https://github.com/lowecg/re-frame-issue-809. • some people want to https://github.com/oliyh/re-graph/issues/75#issuecomment-2478108942. What exactly does that mean?

p-himik 2024-12-02T12:21:01.773389Z

> • when running re-frame on the JVM, ticks are https://github.com/day8/re-frame/blob/f654f42430ac0bad9ad43688ebc9d712fb26b4e6/src/re_frame/interop.clj#L31. Why? Why not just handle them on the main thread, the same was as it happens in thread-less JS?

Kimo 2024-12-02T12:40:18.900739Z

re-frame.interop/next-tick in cljs isn't exactly "main thread" behavior, since it uses goog.async.nextTick

Kimo 2024-12-02T12:42:01.726859Z

I suppose the executor gives us something comparable, providing a rhythm of execution which is somehow nicer for other threads. https://stackoverflow.com/questions/50076815/singlethreadexecutor-vs-plain-thread

p-himik 2024-12-02T12:42:08.815019Z

It's still right there on the main thread in JS, because there's no other thread. Nothing else is going on when a handler fed to nextTick is running. Except for web workers and some other low-level stuff that re-frame doesn't use.

p-himik 2024-12-02T12:42:47.673229Z

And I'm not comparing an executor to an explicit single thread (in fact, re-frame used to use a single thread at the start). I'm comparing it to not using any extra threads at all.

p-himik 2024-12-02T12:44:30.260819Z

In other words, what are the downsides of turning next-tick into

(defn next-tick [f]
  (f)
  nil)
? (And why does the current impl use some args when there are no args?..)

p-himik 2024-12-02T12:49:32.604509Z

If I limit my thinking to JS only, there would be two downsides that I can think of: • UI hanging because nothing would give it a chance to render, since rendering is also done on the main thread • The order of (dispatch ...) (dispatch-sync ...) would become reversed The former is not applicable to Java. The latter is not applicable to the way things are currently implemented. And there are no explicit guarantees about the order of those two things anyway, as far as I'm aware.

Kimo 2024-12-02T12:52:10.051629Z

context - https://github.com/day8/re-frame/pull/183

p-himik 2024-12-02T12:55:00.989229Z

> don't like the idea of calling f directly because it would mean that in your tests you would see synchronous behaviour that in the real-world would be [a]synchronous. I agree in principle but I disagree that it's of any use for tests. JVM and JS are vastly different, testing re-frame on the former can in no way prove that the same tests would end in the same result on JS, and vice versa. Threads are not an async queue that is JS.

p-himik 2024-12-02T12:58:27.563499Z

And given that the issue was created not in the context of tests but in the context of a real app (which makes the PR 183 a bit funny with the "not the goal" opening :) ), I'd say preferring to make things simpler for regular apps as opposed to requiring them using some custom CLJ-only API makes sense.

Kimo 2024-12-02T12:59:19.379859Z

sounds like a good PR.

Kimo 2024-12-02T12:59:57.323049Z

but for now, the executor is in re-frame stable. we should probably just support it. and the current PR is pretty small.

p-himik 2024-12-02T13:00:37.346709Z

By merging the current PR, you'll expand the API thus cementing the executor in place. The fact that something is stable doesn't mean that it should remain there forever.

👍 1
Kimo 2024-12-02T13:01:22.628809Z

yeah. if removing the executor solves the repro, then maybe it's fine. https://github.com/lowecg/re-frame-issue-809

Kimo 2024-12-02T13:06:55.134949Z

https://github.com/day8/re-frame/discussions/686

Kimo 2024-12-02T13:15:14.530009Z

the "concurrency issues" mentioned here might be related to: https://github.com/day8/re-frame/issues/469

Kimo 2024-12-02T13:15:50.003769Z

although that issue seems to be caused by the executor 😛

p-himik 2024-12-02T13:34:06.885909Z

Exactly! So it seems that not just the executor but also the work done in https://github.com/day8/re-frame/pull/471/ could be revisited. It even states my concern in the OP there: > • it works fine in cljs because that's single-threaded > • jvm clojure interop creates a single threaded executor, but that coexists with the main thread for a total thread count of 2 So it doesn't make sense to use another thread to be "closer to CLJS".

Kimo 2024-12-02T13:41:11.549229Z

> • The order of (dispatch ...) (dispatch-sync ...) would become reversed > > The latter is not applicable to the way things are currently implemented. And there are no explicit guarantees about the order of those two things anyway, as far as I'm aware Could you describe this more? I'm not sure how to imagine this happening in practice.

p-himik 2024-12-02T13:44:46.366749Z

What I meant is that if you have code like this

(dispatch [:a])
(dispatch-sync [:b])
then :b would be handled earlier than :a because :a would be on the queue. On the CLJ side, if you get rid of the extra thread, it would :a would be handled first. So, the order is reversed. But with an extra thread, there's no guaranteed order altogether - anything goes.

Kimo 2024-12-02T13:45:27.979999Z

interesting

p-himik 2024-12-02T13:46:31.090279Z

But as far as I recall, re-frame docs don't make any explicit guarantees apart from "`dispatch-sync` handles an event right at this moment".

Kimo 2024-12-02T13:58:10.995759Z

I wonder if there's a way to actually get the same behavior

p-himik 2024-12-02T13:59:14.458289Z

Nothing that would be worth it.