Fork me on GitHub
#clojurescript
<
2021-03-19
>
nilern08:03:32

@lilactown await would just make the surrounding function return a Promise and you would still have the same problem?

nilern08:03:33

Could you just shim cljs.test/async on the clj side or something:

(defmacro async [done & body] `@(do ~@body))
Maybe there should be a proper implementation in clojure.test though

lilactown17:03:27

yeah that maybe would help

lilactown17:03:06

I would need to detect whether I'm in a CLJS or CLJ context when emitting code, since macros are defined in Clojure. Or continue to split my CLJS and CLJ test files

nilern18:03:22

Yes I thought that would be an issue but you can actually do (:require #?(:clj [my.async-shim :refer [async]]) in the tests

zendevil.eth12:03:46

Has anyone tried the http://hamsters.io library for multithreading js? Would you recommend it, or suggest alternatives?

zendevil.eth12:03:22

Would it be recommended to be used in a e.g. react application to boost performance?

p-himik12:03:11

The only possible recommendation with such a vague description could only be to first measure what exactly degrades the performance. I.e. browser profiling. Only then you can start think of proper solutions.

p-himik12:03:39

Also, in browsers there's no backend-like multithreading. Regardless of what library you choose, it will use web workers. Web workers have a bunch of limitations so it's actually very likely that you will see performance degradation and not improvement when using such a library for some scenarios.

dnolen12:03:34

@ps I am skeptical it would make much of a difference

Roman Liutikov12:03:34

for curious, I've made compiler extension a while ago that adds async/await specials https://github.com/roman01la/cljs-async-await not something one would want to use of course, also doesn't remove potential breakage because of intermediate IIFEs generated by the compiler

awesome 3
magnars13:03:25

I think there must be something I’m not understanding about the code splitting feature in cljs. I have two builds sharing much of the same code. After optimization, build A is 900k and build B is 800k. I introduced modules, and now mA is 80k, mB is 40k, but cljs_base is 1080k. How can the shared code (intersection of) A and B be larger than either?

magnars13:03:36

Maybe closure isn’t able to do a proper tree shaking?

thheller13:03:27

it may be larger because the module loader adds a bit of code you otherwise might not have used

nilern13:03:39

You could have some code that is called once from A and once from B and can be inlined and then shrunken by optimizations in the separate builds https://www.cs.princeton.edu/~appel/papers/shrink.pdf

thheller13:03:53

tree shaking and cross-module-montion largely depends on how the code is written

magnars13:03:22

I see. Thanks for the quick responses!

magnars13:03:42

Is there hope that I might see different/better results with shadow-cljs, @thheller, or is it the same underlying mechanism at play?

thheller13:03:35

impossible to say without knowing what your code does. results will vary if the modules use different npm packages. if its purely CLJS code there likely won't be a big difference

👍 4
thheller13:03:19

but you will get build reports that will give you a much better picture about what is in your modules 😉 https://shadow-cljs.github.io/docs/UsersGuide.html#build-report

👏 4
magnars13:03:03

Now that looks interesting. I’ll give it a shot. Thanks!

dnolen13:03:34

@magnars no matter what you do have to build an understanding of how code splitting works to get the best results

dnolen13:03:55

it's not a magical thing - it works best if you have plan around your dependencies and you know where you don't want the heavy stuff to load

dnolen13:03:19

just turning it on isn't going to offer much

dnolen13:03:52

for example you have a React app, but you don't need that for login

dnolen13:03:08

then your login page <30K gzipped

dnolen13:03:45

in general if I'm building something simple, ClojureScript and basic Google Closure Library is all you need

dnolen13:03:22

and the payload is always around jQuery size

dnolen13:03:33

but this is the way to think about code splitting

dnolen13:03:42

what pages need lots of dependencies and which don't

magnars13:03:02

Good points, for sure. I guess I was hoping it would be just a little magical. 🙃 What surprised me was that “extracting common code” would result in more code than either source.

dnolen13:03:17

the loader definitely brings in some machinery

dnolen13:03:45

code splitting for Google Closure stuff is very good

dnolen13:03:51

it will move properties and functions

dnolen13:03:11

but if your dependency graph wasn't planned for the split, nothing interesting is going to happen

dnolen13:03:30

so visual tools are nice here

dnolen13:03:05

but starting out with an actual plan is better - the results are quite obvious if you manage your deps and play around w/ it for a bit

dnolen13:03:55

you do have be careful w/ JS random libraries

dnolen13:03:04

all that can be done is coarse grained moves

dnolen13:03:10

so you're just pushing boulders around

magnars13:03:44

Thanks, that’s helpful. 👍 It’s good to know that I can get better results by reorganizing my code to better align with how the code splitter works.

dnolen13:03:47

for Closure stuff and ClojureScript stuff it can broken apart, but you also have to realize common ClojureScript stuff is always gonna end up in the base

dnolen13:03:32

but even here tricks are possible

dnolen13:03:56

i.e. login page written ClojureScript but you take care to only use JS objects and arrays

dnolen13:03:01

then login page will be bytes

dnolen13:03:02

the hello-world compile test size we have in ClojureScript which seems artificial is useful for confirming that this style of planning can be done

magnars13:03:46

Interesting!

dnolen13:03:59

I tried all this stuff when I originally integrated w/ the Closure code splitter

dnolen13:03:18

so it's not magical, but if you understand it - it can be wielded w/ surgical precision

dnolen13:03:23

I would not bother trying this stuff w/ JS tools, maybe I'm wrong

dnolen13:03:32

but the dep graphs make this type of thing too hard to reason about

magnars13:03:31

Any reading material on the topic to suggest? I guess I can use thheller’s build report to find some culprits and experiment from there.

dnolen13:03:42

not much reading material

dnolen13:03:53

but I would say just play it with it for a couple of days

dnolen13:03:57

and do what I said above

dnolen13:03:15

try doing a login page w/ ClojureScript and no deps, see the output size

dnolen13:03:25

do an inner page w/ React etc.

dnolen13:03:40

not a real app but trivial examples that demonstrate it can be done

dnolen13:03:57

and after that it will be come quite obvious how to do it

magnars13:03:36

Excellent. Thanks for taking the time to explain. Much appreciated.

dnolen13:03:58

bug reports welcome of course too 🙂

🙂 4
dnolen13:03:25

@magnars I see you commented on the loader regression, will take a look at that later

👍 4
magnars14:03:25

Thanks for the link! I’ll add it to the GitHub issue for those who wondered where to look (like me). 👍

magnars15:03:37

To summarize what I understood today: Given two builds A and B, in a perfect world the size of (module A + base) = size of A, and the size of (module B + base) = size of B. However, we don’t have a perfect (magical) code splitter. To get a good split, the code needs to be organized (via namespaces) along the same lines as the modules. Better alignment yields better splits.

thheller15:03:46

technically closure is able to split namespaces as well and move things wherever they are needed but certain things prevent that from working (eg. defmethod can't be moved)

👍 4
thheller15:03:33

but yeah carefully designed code and namespaces split better in practice

fsd18:03:40

Hi There, I had a quick question, I am trying to disable a HTML button using ClojureScript

(def zoom-in-button  (-> js/document (.getElementsByClassName "mapboxgl-ctrl-zoom-in")))

 (if (= max-zoom (:zoom viewport))
    ((first zoom-in-button)(.setAttribute "disabled" "disabled")))
Trying to do
<button class="mapboxgl-ctrl-zoom-in" type="button" title="Zoom In" disabled =""></button>
But keep on getting this error Uncaught TypeError: "disable".setAttribute is not a function In JavaScript
let ZoomIn = document.getElementsByClassName("mapboxgl-ctrl-zoom-in")[0];
ZoomIn.setAttribute("disabled","disabled")

lilactown18:03:22

((first zoom-in-button)(.setAttribute "disabled" "disabled")))
this is the equivalent JS:
ZoomIn[0]("disabled".setAttribute("disabled"))

fsd18:03:09

Ahh it’s calling .setAttribute on array element :thinking_face: Am I right ?

lilactown18:03:43

no, it's calling .setAttribute on the string "disabled", like your error message says

lilactown18:03:33

if we break it down piece by piece,

(.setAttribute "disabled" "disabled")
literally translates to JS:
"disabled".setAttribute("disabled")

lilactown18:03:54

which is throwing the error you see

lilactown18:03:28

you want to call .setAttribute on the zoom-in-button, not the "disabled" string

lilactown18:03:13

the .method syntax in ClojureScript calls the method on the first argument

lilactown18:03:27

(.setAttribute x "disabled" "disabled")

fsd18:03:19

I am kinda new in ClojureScript and trying to wrap my head around this

fsd19:03:28

would you please give me little help how I can call on .setAttribute the element ?

lilactown19:03:38

CLJS:

(.setAttribute button "disabled" "disabled")
will be the same as JS:
button.setAttribute("disabled", "disabled")
does that help?

fsd19:03:00

Ohhh I get it Thank you @lilactown

lilactown18:03:32

do you see the problem?