Fork me on GitHub
#re-frame
<
2015-08-04
>
meow00:08:23

@mikethompson: good question. I've actually been assuming that you'd want to use it in conjunction with your existing event channel, which is certainly possible, but I suppose that combination should be examined as well.

meow00:08:33

So, goog.async.nextTick is a simple one-off callback style operation. You pass it a func to callback and it calls it as soon as possible by injecting a custom event into the js event system. So it gets called when js has gotten to it, which is asap, but allowing js to do all its gui bits.

meow00:08:43

It is certainly possible for re-frame dispatch to be rewritten to use goog.async.nextTick.

meow00:08:39

The question then becomes, is there an advantage for dispatch to continue to put! onto an event channel?

meow00:08:36

What isn't happening with the event channel is the use of a transducer or buffer, because neither is really applicable. So now the channel is really just an async queue, and goog.async.nextTick already provides async behavior.

meow00:08:01

If there are going to be a lot of events dispatched per frame, then calling goog.async.nextTick for each one is probably not going to be efficient. And that is one problem that needs solving. So an event channel should help with that.

meow00:08:24

Basically it toggles some indicators to tell that it's in the middle of running and then uses a single call to (goog.async.nextTick process-messages) to process what's queued up in the channel so far, does a maximum of 1024 (def TASK_BATCH_SIZE 1024) and returns/repeats.

meow00:08:14

So something like that would probably be a good model. When a call is made to re-frame's dispatch, it adds the event to the channel, then triggers a call to goog.async.nextTick with a function that processes whatever is in the event queue, unless there is already a call to that function underway.

meow01:08:16

The question then becomes, how do you know if there is something in the channel. The way they did things in the core.async dispatcher is one way.

meow01:08:51

But I'm thinking there might be an easier way.

meow01:08:24

Something like this:

(defn next-tick
  [callback]
  (letfn [(step []
                (put! event-chan ::re-frame-checkpoint)
                (if (callback) (goog.async.nextTick step)))]
    (goog.async.nextTick step)))

;; start event processing
(next-tick process-events)

meow01:08:19

Then in process-events you would have your go-loop that would <! events off the channel until it got to one that matched ::re-frame-checkpoint and then it would return. Since we always write that event as the first thing in our step function we know it will always be there.

meow01:08:54

You have more flexibility because you can put things in your own event channel.

antishok01:08:06

when would process-events return false? or true?

antishok01:08:27

(been following this discussion). hi

mikethompson02:08:11

@meow: I'm curious ... what makes you say that calling goog.async.nextTick for each event would be inefficient? (More inefficient than putting each event onto a core.async channel). I had the opposite impression. I had the feeling that it would be MORE efficient than using core.async ... after all the browser will then handle the queue in C++. But this is just an impression -- no experience to back it up. I'd be more worried that goog.async.nextTick handled events in the order supplied. I wonder what guarantees there would be around a the underlying goog.async.nextTick queue being FIFO.

meow03:08:43

@antishok: I borrowed the basics of that code from some experimenting I've been doing that is independent of re-frame. In order to make that loop breakable so that I could turn on/off the processing I set it up so that the callback (in this case process-events) had to return a truthy. One use for this is so that hot re-loading code works as expected such as with boot reload or figwheel. I also can manually turn it on or off with a keybinding I have set up.

meow03:08:57

I hadn't really thought through if/when process-events would return a value in this example.

meow03:08:29

@mikethompson: I have nothing to back up that assertion other than an impression, so it would be good to test it. I just tend to think of puts to a channel as cheap.

meow03:08:19

Plus you'd be giving control back to js.

mikethompson03:08:35

That ticket might not be the ultimate solution, but it might get us closer

antishok03:08:10

@meow: the reason I asked is because it seems to me that if process-events always returned true, then next-tick would be looping endlessly putting the ::-re-frame-checkpoint on the channel and then removing it inside process-events. and this looping would be occuring even if no events at all were being dispatched. might've misread though

meow03:08:53

@antishok: no, that is 100% correct

meow03:08:32

I don't think it would have much impact but that is something that could be checked and worked around.

mikethompson03:08:50

BTW, this repo contains interesting, related info: https://github.com/YuzuJS/setImmediate

meow03:08:27

That's the kind of sophistication that the core.async dispatching has built in so that it doesn't dispatch unnecessarily.

meow03:08:22

@antishok: so far I've been using that kind of code on channels that supply an infinite stream of data, so it hasn't been an issue.

meow03:08:11

@mikethompson: glad you made that ticket - was thinking that core.async needed a better approach as well, but goog has goog.Timer.callOnce which would work for any amount of time.

meow03:08:50

@mikethompson: Here's one thing I was thinking. If someone had a re-frame app and was dispatching hundreds of events per frame then it just doesn't seem like doing an async call for each one that yields to the js event loop is going to be possible, but that's just an impression. Versus writing those to a channel where they will be taken out of the channel and handled inside one goog.async.nextTick.

mikethompson03:08:31

In case of emergency, break the glass and use dispatch-sync

meow03:08:32

Not saying that I'm right, just what lead my thinking.

meow03:08:23

Can you believe that here we are in the year 2015 and we're having to deal with this browser nonsense? simple_smile

mikethompson04:08:10

@meow: don't start me simple_smile

pupeno09:08:04

I’m new to re-frame and I have a question on how to structure code.

pupeno09:08:31

I’m starting from the template, with secretary. I have handler called set-active-panel that is dispatched when the URL changes.

pupeno09:08:30

When the active-panel is :home-panel, I want to fetch some data from the server. My understanding is that I should register a handler for that and trigger an event, but where is the right place to trigger the event? if I do it inside set-active-panel, it’ll turn into a huge dispatching function (maybe eventually into multi-methods). Is that the correct approach?

caskolkm09:08:18

@pupeno: we trigger it in the routes of secretary

(secretary/defroute companies "/companies" {:as params}
  (dispatch [:request-companies])
  {:view [:companies]
   :params params})

pupeno11:08:56

mikethompson: was that to me?

mikethompson11:08:05

Hmm. Rereading your question ... i think I got the wrong idea about what you wanted to know

mikethompson11:08:55

So ... you have some views and each one needs different data from the server?

pupeno11:08:24

Not each, but many will require getting some data from the server.

mikethompson11:08:49

So perhaps you need to do something like (dispatch [:set-home-panel])

mikethompson11:08:03

And the handler for that event knows it needs to load data from the server?

mikethompson11:08:53

So it has two things to do: - set the view (some piece of state) - initiate the server load

mikethompson11:08:16

So in the todomvc code I linked to above there's this: (defroute "/:filter" [filter] (dispatch [:set-showing (keyword filter)])) you'd modify that to be something like (defroute "/:filter" [filter] (dispatch [(keyword filter)]))

mikethompson11:08:02

Now you need to provide a handler for each view?? That's one approach.

mikethompson11:08:13

Sorry, I have to head off

pupeno11:08:17

A handler for each view is not bad.

shaun-mahood15:08:12

@mikethompson: Is there a good way to retry a dispatch that doesn't exist until it succeeds? I have an initial dispatch that starts things off and a couple of the events it dispatches aren't always there even though their definition file is declared earlier. Only causes problems intermittently but is a pain when it does.

jhchabran16:08:41

Hi I’ve fixed my previous issues with issuing too much dispatch calls by throttling our event source (with core.async) as pkobrien and mikethompson suggested (even one per second is enough actually, there’s in fact no need to display things like current altitude 200 times per second to user). Continuing to make POCs of different features we need for our app, I’ve added a component that wraps a google map. I based myself on https://github.com/reagent-project/reagent-cookbook/blob/master/recipes/google-maps/src/cljs/google_maps/core.cljs but it doesn’t provide any example of things like, updating a marker position according to some data in app-db. So I went on and wrote the following component, https://gist.github.com/jhchabran/e09883c3bc1b703a224d . I added questions/reasoning in the comments. I’m really unsure if I got it right, so any input would be valuable simple_smile

antishok16:08:53

@jhchabran: as for points 1-3 I thought I'd just mention that technically your run! is not in the render function. only the :reagent-render fn is a render function

jhchabran16:08:31

antishok: I’m gonna re-read my comment, I didn’t mean to suggest it was (I know that it isn’t)

antishok16:08:59

and let inside let is definitely not wrong in this case

jhchabran16:08:16

what I mean, is if I discard the whole idea of using run!, and just update the marker/panning inside the render, by simply using pos inside :reagent-render, it worked

antishok16:08:27

ah yeah that would be wrong probably

jhchabran16:08:32

updated the comment accordingly, thanks simple_smile

jhchabran17:08:50

overall, I feel pretty confident following the readme to build an app that interacts with some REST api, but I’m afraid of shooting myself in the foot there with the gmap, especially since we will have other libs that embeds things in the dom to add

jhchabran17:08:10

antishok: btw thanks simple_smile