-
-
Notifications
You must be signed in to change notification settings - Fork 368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support building go-app with GopherJS (no wasm) #819
Comments
What's the advantage of doing this besides the bundled JS that can be included directly in the page? |
GopherJS has 32 bit int, which would give some of my apps problems. But I am also not sure how this will work when using GopherJS because afaik the go-app frontend code is running in another thread (from the service worker). I wonder how well GopherJS handles concurrency. You write it gets loaded in the page, wouldn't this be inside the UI routine of the browser. I may not understand enough about that topic though, but I read that GopherJS has quite some overhead for channels (because of blocking code not being possible in the main browser js thread). |
You could bundle the WASM too but the whole PWA aspect is about using the fetch cache interface of the web worker so it does not get loaded again and again, as it would be if you embed it into the html for the site. |
Right now I just don't see how GopherJS and WASM are even compatible 🤔 |
To be honest folks, gopherjs might be interesting but it is Go transpilled to js and there might be compatibility issue. For me the problem is a bit like with tinygo. Maintaining it with Go official support is already a lot of work and I don’t have the time and resources to add tinygo or gopherjs on the top of that 😅. |
Hi folks, gopherjs maintainer here 🙂 Let me be a bit more background to this request, and maybe a reason to reconsider the decision. GopherJS has a playground similar to the one Go itself offers. One of its features is that it is compiled by gopherjs itself, which allows it to run fully client-side, in pretty much any browser. It serves a double-duty as one of our reference apps we use to test gopherjs against. The playground though is due an overhaul. Currently it's based on a very old and very unsupported version of AngularJS. So before we begin adding features we wanted to rewrite the playground on a more modern and actively evolving framework. And it seemed elegant to pick one of the Go-based options instead of making bindings to something like React. @akolybelnikov suggested go-app as an option except that, of course, it currently only targets wasm. To test feasibility of the idea I threw together a quick proof-of-concept, https://github.com/maxence-charriere/lofimusic seemed like a complex enough app to catch most issues. Turns out, there is very little that needs to change in go-app to make it work with gopherjs. This commit is a quick-and-dirty demonstration of that:
This all works because gopherjs provides full support for the standard So I think being compatible with gopherjs won't put too much burden on you, most of it is born by gopherjs being compatible with Go Wasm. In theory, some performance could be gained by writing a gopherjs-specific version of https://github.com/maxence-charriere/go-app/blob/master/pkg/app/js_wasm.go using the As a side note, the lofimusic app compiled with Go Wasm is 3.3M (gzipped) and GopherJS is 1.7M (also gzipped). That can be a significant appeal in cases where reducing asset size is a concern. Please let me know what you think 🙂 |
Well, we have some much larger and more complex applications than lofimusic. It would be interesting to check out if they work. One public proof of concept is at https://github.com/oderwat/go-nats-app. Do you care to create a PR to make this compile with GopherJS? If that works, I may try out some of our larger applications that are a lot bigger than this demonstration. P.S.: Going JS looks backwards to me. I would prefer more (Go) people would work on WASM, which has quite some interesting feats for the future. |
I ll have another look and evaluate this. |
@oderwat here you go: https://github.com/nevkontakte/go-nats-app. If you want to play around with my fork locally you need to install gopherjs from the latest master:
I get that a lot, which reminds me every time that GopherJS has ways to go with regards to explaining its purpose and general PR. In short, compiling to JS has its own unique benefits, for example interop with native JS code (which is still going to be lingua franca of web frontends for years to come), broader browser support. It also used to outperform Go Wasm, but the most recent benchmarks I know of are from before I got involved with the project, so I don't know if this is still the case. This is the same kind of question as "why Go supports risc-v architecture, arm is clearly superior" ¯_(ツ)_/¯ |
@nevkontakte very cool. I will have a look at this. I hope we can agree that it is fine to use what is "mainstream" and still be able to see the flaws and work on replacing it with something that is suited better for the task at hand. Likewise, I look forward to more projects like Scale and debugging, real threading and network access for WASM in the browser. P.S.: I guess you meant "why go supports arm and not risc-v". Well, I guess it supports what is being running on the majority of computers we use. |
@nevkontakte I checked out the GopherJS and I am impressed. I hope I get some time to check this with some other projects of us. It is fascinating that it works and even when the uncompressed code seems to have a similar size (8-9 MB), the gzip version of the JS variant is about 50% of the WASM version. Definitely something that makes sense in production. I have a complex app that runs a bit sluggish on old Android devices. I guess this will be the first target for me to test in a GopherJS version. |
@oderwat I'm getting off topic, but Go actually does support risc-v, specifically |
@maxence-charriere have you had a chance to consider this request? I'll have some spare time next weekend, which I could spend putting together a proper PR. |
@nevkontakte Sorry, but I forgot to report back from trying to build one of our larger projects. Here is the short version of the journey: I had to basically delete "../../../../../../sdk/go1.18.6/src/net/http/pprof/pprof.go" because of
Then I got an error about the missing Next I had Go 1.18 vs Go 1.120 related problems:
Finally, I ended up with another problem in our AvroX package:
This makes all of our PWAs impossible to compile with GopherJS as we use AvroX quite everywhere. But I just removed all of it from one of the PWAs because I was curious what happens next. Which then was the int32 problem (as I stated at the very beginning of this issue)
So, I removed the heart of that App's functionality and continued to compile. It then compiled, and I could try to run the app. While it initializes, it throws:
I think that is related to using the runtime for finding the call-stack for our dbg package. I could go and fix that too. But I stopped here and changed back to the WASM backend and compiled the same completely crippled code, and it just started as usual (minus the actual functionalities). I fear we most likely will never be able to use GopherJS with Go-App in anything that goes into production. We do use GopherJS in that same project for creating some (rather toy examples) of frontend JavaScript to handle some buttons, and I was impressed how it magically works. But there it adds a ton of JS code for something that needs 10 lines of native JS. Some other consideration at the end: The crippled app compiled into 8.4M + 487K (map) for JS and 9.1M for WASM. I don't see that this would make us consider using JS instead of WASM, either. Even if the full working app currently compiles to 16M, I doubt that having all the code compiling to JS will shave off enough size, that a rewrite of all the problematic parts would be feasible. |
@oderwat thanks for the report, this is interesting to know. Let me give a brief response, and I'll be happy to have a more detailed discussion with you elsewhere (feel free to file an issue in the GopherJS issue tracker or ping me in the Gophers Slack)
|
@maxence-charriere even if you don't want to support GopherJS officially, I think it is possible to make a few changes that will make it at least not actively incompatible, and the interested users would be able to inject the required initialization scripts themselves. That will be sufficient for our purposes, and won't put any additional burden on you. |
I ll take a look on this by the end of the month. |
This is really interesting stuff… I am surprise that gopherjs can do this. i think that it’s best to give more time for evaluation of gopherjs. Its maybe just me but I feel that it’s worthwhile to let things evolve a bit … I mean it’s amazing it works and or course there will be some rough edges. On a side note. I don’t know if anyone has considered this but there might be advantages of even having the PWA service worker in wasm and the DOM Renderer in gopherjs. There are a few use cases where separating the two is useful from a concurrency / threading point of view. my own worry is that gopherjs has only a few maintainers ? whereas golang wasm is more supported by the golang team. Tinygo is also financially supported AFAIK by google. |
@nevkontakte Have you considered the possibility of maintaining any gopherjs compile issues in go-app ? I only ask because @maxence-charriere would be having to support 2 compiler paths which is more work !! |
I was not able to use What I'm thinking is to introduce a It is a bit unclear to me what are gopherjs requirements. I have a few questions:
|
@maxence-charriere to get the right gopherjs you need to install it like this:
Notice the replaced Go-App dependency in the From what I see, you need to switch the build tags and then just need to load the GopherJS
So, he is using your version right now, but that could be probably optimized for GopherJS. |
@nevkontakte I guess for us, it comes down to generics support and newer Go compilers. This is what makes rewriting the code impossible for us. And you are right: We should use int64 in the parts that really need int64, I actually consider rewriting it (for other reasons too). I am not sure what the |
@nevkontakte mentioned that the profiling can be made to be agnostic based on build time or runtime tags. it’s mentioned higher up in this issue .. “ net/http/pprof few packages we don't support properly - for historical reasons. I'm pretty sure it should be possible at least make a compilable no-op implementation, but nobody complained about it, so it has been pretty low on my priority list. “ |
Wow, a lot to respond to 🙂
That is correct. Besides myself, there's another person who (for real life reasons) at the moment can only really provide code reviews, but doesn't have enough time to contribute code. I would happily onboard more people, but it's been hard to find volunteers so far. Also, gopherjs is not my full-time job, so it gets much less time than it deserves. Which leads me to...
I have, and I'm somewhat on a fence about this. On one hand, I don't think that would be that much work, and it's a nice change of pace from developing gopherjs itself. On the other hand, I don't want to give promises I am unsure I'll be able to keep. And gopherjs itself takes pretty much all the time I have for pet projects.
So the theory goes that any program that works under wasm and doesn't use unsafe should work under gopherjs. There are three places where the theory doesn't match reality:
My thinking was that a few simple changes in go-app API can address points #1 and #2. For #2 specifically it could provide an option for the alternate initialization script, in the same way it already does for the service worker, and with the same warning. Then for @maxence-charriere the support story doesn't change - wasm would be the main targeted architecture, but those brave enough to tinker can use other toolchains - and enjoy the debugging :) I think at this point it would be more productive for me to actually put together a strawman PR instead of handwaving, and then we could have this discussion more concretely.
No, its output is self-sufficient and can be included directly in the page with a
GopherJS should support those functions, with the limitation that |
@nevkontakte strawman PR 👍 Like you said it will help move things forward with less unknowns. I am really interested in leveraging both in the one app with wasm for the Service Worker and Web Workers and JS for the Rendering. Its is the best use of both IMHO and could lead to higher perf. I have not looked into the BUS in go-app between the SW and DOM layers. I assume there is an abstracted one in the code base @maxence-charriere ? |
@oderwat then the good news is - both are coming. I hope to finish work on generics within the next a couple of months, then catch up on Go releases. |
As promised, #830 is a cleaner way of using go-app with gopherjs. I took @maxence-charriere idea of implementing a Let me know if that looks reasonable to y'all. |
I can still compile to WASM with that branch and no changes to my pretty complex setup, makes me happy ;-) |
Currently go-app assumes that the target architecture for the client side must be
GOOS=js GOARCH=wasm
. Here is a POC of the Lofimusic app build with GopherJS with a minor patch to the go-app:https://github.com/nevkontakte/lofimusic/tree/0d328bf50b6fe7a3fc5f1a5d0d06a99f3e7cc368
The compiled code for GopherJS looks very different from Go-wasm, but it supports all the same APIs, including "syscall/js". The only major difference between Go-wasm and GopherJS is how the code is initialized: GopherJS produces a self-sufficient JS script, so it only needs to be included in the page via a <script> tag.
If as a go-app user you want to make a choice at the build time whether you want to use GopherJS or wasm, the framework could generate the appropriate initialization scripts.
As an aside, although GopherJS does support
syscall/js
API, it is a wrapper around the github.com/gopherjs/gopherjs/js package and is a bit slower than using the latter directly. It should be easy to make a GopherJS-specific implementation of that forGOOS=js GOARCH=ecmascrit
target, thus gaining some performance.The text was updated successfully, but these errors were encountered: