Fork me on GitHub
Joe Dumont05:07:32

Hi. I'm playing around with the canvas element and stuck on some rendering issue. The following code is triggered at a button press. It compiles without errors, the console logs the message, but the canvas doesn't update. I can then run the code from (set!...) to (.fillRect...) in the repl and it will correctly appear in the canvas element however.

(defn draw-board
  [board canvas]
  (js/console.log "drawing board")
  (set! (.-fillStyle (:ctx canvas)) (:color-1 canvas))
  (for [[idx cell] (keep-indexed (fn [j val] (when (pos? val) [j val])) board)
        :let [row (int (/ idx row-width))
              col (mod idx row-width)]]
    (.fillRect (:ctx canvas) (* row res) (* col res) res res)))
Similarly, when I click on the "stop" button, the following code is executed. I'll get the console message, and the buttons appropriately disabled/enabled, but the canvas won't clear. If I then run the (for ...) expression in the repl, it clears everything as desired. What am I missing between the behavior in the repl vs executed in these function blocks? Thanks for your insights!
(defn end []
  (js/console.log "ending")
  (for [ctx (reduce
             (fn [acc el] (conj acc (:ctx el)))
             [canvas-1 canvas-2 canvas-3])]
    (.clearRect ctx 0 0 width height))
  (set! (.-disabled (.getElementById js/document "stop")) true)
  (set! (.-disabled (.getElementById js/document "start")) false))


@dumont for is lazy and does not execute since you never consume the result. just swap for with doseq and it'll do what you expect

🙌 1

Oooh! I have been using doall. Is that adequate as well? @U05224H0W


doseq is just for the side effects and has no return value. if you need the return value then you need for.

Joe Dumont05:07:05

Aha! Thank you.


@roman01la (+ Pitch crew) — I know you’ve invested a bunch in performance lately… I would be super curious to understand how you’re identifying bottlenecks. Do you profile in a synthetic environment? Do you instrument in production?

Roman Liutikov08:07:18

Hey. Custom performance metrics on the client (DataDog) + customer feedback + internal feedback via dogfooding. We didn't have any success with synthetic performance testing, I know some are running Lighthouse on CI to measure loading time and Web Vitals, but we are mostly interested in runtime performance (UI interactions, navigation, FPS while moving stuff, etc.)


yeah we’re also more interested in runtime performance. One thing we’re doing is pointing a bunch of chrome instances at the same shared space to generate and measure behavior under load. That does provide some insight but I think we haven’t quite figured out how to capture runtime performance metrics. Do you measure component render times or stuff like that? Curious what you’re tracking specifically

Roman Liutikov09:07:24

We are tracking UI interactions and router navigation. It's time from user input (click) to when expected UI was painted. The measurement is done via performance.mark/measure.

👍 1

Here’s a strange question I don’t fully understand


(defn get-questions
  "Call the API and return the data in clj"
  (-> (.fetch js/window (str config/api-server-base-url "/v1/questions"))
      (.then #(.json %))
      (.then (fn [resp] (-> resp
                            (js->clj :keywordize-keys true))))


the first (tap>) get’s me the response as a Clojure map


however the 2nd (tap>), just gets me the Promise


#object[Promise [object Promise]]


I would have expected it to be resolved by then and return me the proper Clojurescript map?


my goal is to return the Clojurescript map to the caller


The promise is resolved. But it's still a promise. Promises never get "unpacked" within the context where you use them. .then exists specifically for you to be able to access those "packed" values.


hmm, that is interesting


it just feels ugly to call rf/dispatch right there in the calling


instead of upon return to the caller


how would you approach this?


Well, no way around that - that's just how JS operates. I'd just do exactly what you deem ugly. :) It's both the simplest and the easiest solution.


thanks 🙂


And I'd also change that docstring to reflect the fact that the function does not really return the value itself. ;)


haha yeah unfortunately


I’ve now done it that way


however in the caller when I subscribe to the result


I get nothing on the first request


(e/get-questions!) (let [questions @(subscribe [:questions])] …)


I understand this is likely due to async, and the 2nd call actually does return the expected count of 3 i’m looking for


@U2FRKM4TW how would you resolve this?


That's not a problem by itself because you are not supposed to use subscriptions outside of a reactive context (i.e. views or other reactions used in views). When you update app-db with a help of an event, any deref'ed subscriptions will notify their reactive context in due time, and those contexts will update themselves (views will be re-rendered, reactions will be recomputed - but only if they're themselves deref'ed in a reactive context).


You are absolutely right! I totally forgot about that, sorry.

👍 1