Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Use of goog.async.nextTick prevents reagent from rendering #480

Closed
p-himik opened this issue Jul 5, 2018 · 3 comments
Closed

Use of goog.async.nextTick prevents reagent from rendering #480

p-himik opened this issue Jul 5, 2018 · 3 comments
Assignees

Comments

@p-himik
Copy link
Contributor

p-himik commented Jul 5, 2018

Minimal complete example at https://github.com/zalky/re-demo (thanks, @zalky).
A bare-bones example that shows the root cause:

(ns re-demo.core
  (:require [reagent.core :as r]
            [reagent.ratom :as r*]
            [re-frame.interop]
            [reagent.impl.batching]))

(def result (r*/atom 0))

(def use-bad-next-tick? true)

(defn work [tasks]
  (js/console.log "Doing work")
  ;; Just do some busy waiting
  (reduce + (range 100000))
  (reset! result (first tasks))
  (when-some [tasks (next tasks)]
    (let [f #(work tasks)]
      (if use-bad-next-tick?
        (re-frame.interop/next-tick f)
        (reagent.impl.batching/next-tick f)))))

(defn root []
  (fn []
    (js/console.log "Rendering" @result)
    [:div "Result: " @result]))

(defn init! []
  (r/render [root] (.getElementById js/document "container")
            #(work (range 1000000))))

The issue is that goog.async.nextTick (used by re-frame.interop/next-tick here) uses MessageChannel which appears to be much faster on Google Chrome than requestAnimationFrame that's used by reagent to schedule renders.
It makes the browser wait for multiple seconds before actually rendering stuff.

This comment #164 (comment) mentions that it may be useful to switch to promises. Not sure whether it's relevant here, just wanted to leave a reference.

@p-himik
Copy link
Contributor Author

p-himik commented Jul 5, 2018

A related issue: #382

The main concern is that using ^:flush-dom may be ineffective if used for every event in chunking, and computing batch sizes so that ^:flush-dom is attached only once per frame can be rather tricky.

@danielcompton
Copy link
Contributor

danielcompton commented Jul 7, 2018

Thanks for this, don't have a lot of time to dig into the nitty gritty details here at the moment though, but am interested in looking further at it. See also binaryage/cljs-devtools#20 (comment) for some more details on this area.

@superstructor
Copy link
Contributor

Although reagent.impl.batching/next-tick and re-frame.interop/next-tick share the same name, they don’t attempt to serve the same purpose. re-frame’s next-tick wants to schedule the function to run ASAP, whereas reagent’s version will schedule the function to run as part of the next animationframe which might be 16ms away.

Note reagent.core/after-render calls reagent.impl.batching/do-after-render which is in turn placed onto the RenderQueue to run via reagent.impl.batching/next-tick.

The implementation of ^:flush-dom is a composition of both these fns via reagent.core/after-render and then re-frame.interop/next-tick. This is so that the event marked with ^:flush-dom is called as soon after render as possible (i.e. re-frame.interop/next-tick), but not immediately after render (i.e. reagent.core/after-render by itself).

Therefore you will not see the same behaviour with ^:flush-dom as you see in the example provided above, because the example above uses one or the other, but not both in composition.

Closing.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants