Skip to content
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

How to make wasm reactive? #203

Closed
mar1n3r0 opened this issue May 18, 2021 · 7 comments
Closed

How to make wasm reactive? #203

mar1n3r0 opened this issue May 18, 2021 · 7 comments

Comments

@mar1n3r0
Copy link

mar1n3r0 commented May 18, 2021

The way Go treats wasm as an application means it runs and exits. This is quite limiting compared to modern JS frameworks with their reactive APIs which allow real-time reactivity on external changes.
Is this even possible with the current state of things? Maybe keeping dedicated channels open infinite? This would allow all kind of things from state management stores to webhooks to websockets etc.

Go takes a different approach, Go treats this as an application, meaning that you start a Go runtime, it runs, then exits and you can’t interact with it. This, coincidentally, is the error message that you’re seeing, our Go application has completed and cleaned up.

To me this feels more closer to the .NET Console Application than it does to a web application. A web application doesn’t really end, at least, not in the same manner as a process.

And this leads us to a problem, if we want to be able to call stuff, but the runtime want to shut down, what do we do?

https://www.aaron-powell.com/posts/2019-02-06-golang-wasm-3-interacting-with-js-from-go/

Here is an example:

I would like to connect the app to an external state management store and make it re-render on state changes.
By the time the state changed the runtime has exited, hence unless there is a channel open to keep it alive it can't react to external events after exit.

@bradleypeabody
Copy link
Contributor

Vugu does re-render on state changes. The way it works is the main() blocks on a channel in it's main loop and there is some glue under the hood that drives the wasm application by doing various calls back and forth from JS->WASM. Logically your application "continues to run" and stays alive for as long as you like. Under the hood it's broken up into various calls back and forth.

If you're interested in that part, have a read through https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js

But for practical purposes, it appears to work like any other Go program and keeps running in a loop for as long as the page is open.

@mar1n3r0
Copy link
Author

mar1n3r0 commented May 18, 2021

The question is what happens when you have multiple components working with the same state altering it async. Once an app outgrows the single component behavior and has many async operations this becomes quite hard to handle. Most of the time shared structs have nil pointers when dereferencing because the reference is not there yet due to the multiple async calls. It all comes down to being able to manage state separately and re-rerender only the components that receive an updated state automatically as the state changes. Without a state store and reactivity that is comparable to nowadays JS frameworks no one would consider wasm for production.

@Nv7-GitHub
Copy link

You would use an EventEnv for that. You call Lock() whenever you change a variable and UnlockRender() when you are done changing the variable and ready to re-render.

@mar1n3r0
Copy link
Author

mar1n3r0 commented May 18, 2021

OK, let's take the fetch and display example.

https://github.com/vugu/vugu/blob/24d8d4332371618c6f2b8881ae9f8d52fab8e3ca/examples/fetch-and-display/root.vugu

Say we use Root.bpi in another component at the same time. In my experience during the async call the value there can not be dereferenced until the call completes so it returns a nil pointer in that other component while being locked in the current one.

@Xumeiquer
Copy link

Hi,

You can see that I am using a component that acts as the main state component. https://github.com/Xumeiquer/wallets/blob/main/webapp/middleware/state.go.

On the other hand, the element I want to be sync across the application is stored as a pointer like in this case https://github.com/Xumeiquer/wallets/blob/main/webapp/components/header.vugu#L171, Then, I check and store a local copy on the Init function of each component that requires to use it https://github.com/Xumeiquer/wallets/blob/main/webapp/components/dashboard/dashboard.vugu#L28. Also, I update the value from the store in the Compute method just in case the object in the State was null at the moment of its initializtion.

I am sure that implementation can be improved. But it does the job.

@mar1n3r0
Copy link
Author

The solution has been standardized it seems:
https://gobyexample.com/stateful-goroutines

I will try to create a package for it that is universal for all Go wasm frameworks and share it with you.

@owenwaller
Copy link
Collaborator

Closing this issue as this looks like there is a solution.

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

No branches or pull requests

5 participants