docker container run -dP nlepage/golang_wasm:examples # Find out which host port is used docker container ls
helloworld.go
and compile it with the following command: GOOS=js GOARCH=wasm go build -o test.wasm helioworld.go
Dockerfile
file like this to compile: FROM nlepage/golang_wasm COPY helloworld.go /go/src/hello/ RUN go build -o test.wasm hello
wasm_exec.html
and wasm_exec.js
files available in the go repository in the misc/wasm
or in the docker nlepage / golang_wasm image in the /usr/local/go/misc/wasm/
to run test.wasm
in browser (wasm_exec.js expects binary test.wasm
, so we use this name).test.wasm
on only if test.wasm
loaded correctly).test.wasm
must be serviced with the MIME type application/wasm
, otherwise the browser will refuse to execute it. (for example, nginx needs an updated mime.types file ).wasm_exec.html
and wasm_exec.js
in the code> / usr / share / nginx / html / directory.js.go
js.Value
type is js.Value
, which represents the JavaScript value.js.Value.Get()
and js.Value.Set()
return and set the values ​​of the object's fields.js.Value.Index()
and js.Value.SetIndex()
refer to the object by index for read and write.js.Value.Call()
calls an object's method as a function.js.Value.Invoke()
calls the object itself as a function.js.Value.New()
calls the new operator and uses its own knowledge as a constructor.js.Value.Int()
or js.Value.Bool()
.js.Undefined()
will give js.Value the corresponding undefined
.js.Null()
will give js.Value
corresponding null
.js.Global()
returns js.Value
giving access to the global scope.js.ValueOf()
accepts primitive Go types and returns the correct js.Value
window.alert()
. alert := js.Global().Get("alert")
alert
variable, in the form of js.Value
, which is a reference to window.alert
JS, and you can use the function call via js.Value.Invoke()
: alert.Invoke("Hello wasm!")
interface{}
and passes values ​​through ValueOf itself. package main import ( "syscall/js" ) func main() { alert := js.Global().Get("alert") alert.Invoke("Hello Wasm!") }
test.wasm
, and leave wasm_exec.html
and wasm_exec.js
as it was.examples/js-call
folder.syscall/js
package, the second file to view is callback.go
.js.Callback
type wrapper for the Go function, for use in JS.js.NewCallback()
function that accepts a function (accepting a js.Value
slice and returns nothing), and returns js.Callback
.js.Callback.Release()
, which must be called to destroy the callback.js.NewEventCallback()
similar to js.NewCallback()
, but the wrapped function takes only 1 argument - the event.fmt.Println()
from the side of JS.wasm_exec.html
to be able to get a callback from Go to call it. async function run() { console.clear(); await go.run(inst); inst = await WebAssembly.instantiate(mod, go.ImportObject); // }
Promise
state to completion: let printMessage // Our reference to the Go callback let printMessageReceived // Our promise let resolvePrintMessageReceived // Our promise resolver function setPrintMessage(callback) { printMessage = callback resolvePrintMessageReceived() }
run()
function to use the callback: async function run() { console.clear() // Create the Promise and store its resolve function printMessageReceived = new Promise(resolve => { resolvePrintMessageReceived = resolve }) const run = go.run(inst) // Start the wasm binary await printMessageReceived // Wait for the callback reception printMessage('Hello Wasm!') // Invoke the callback await run // Wait for the binary to terminate inst = await WebAssembly.instantiate(mod, go.importObject) // reset instance }
var done = make(chan struct{})
printMessage()
function: func printMessage(args []js.Value) { message := args[0].Strlng() fmt.Println(message) done <- struct{}{} // Notify printMessage has been called }
[]js.Value
, so you need to call js.Value.String()
in the first slice element to get the message in the Go string. callback := js.NewCallback(printMessage) defer callback.Release() // to defer the callback releasing is a good practice
setPrintMessage()
function, just like when you call window.alert()
: setPrintMessage := js.Global.Get("setPrintMessage") setPrintMessage.Invoke(callback)
<-done
package main import ( "fmt" "syscall/js" ) var done = make(chan struct{}) func main() { callback := js.NewCallback(prtntMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback) <-done } func printMessage(args []js.Value) { message := args[0].Strlng() fmt.PrintIn(message) done <- struct{}{} }
test.wasm
. You also need to replace wasm_exec.html
with our version, and wasm_exec.js
can be reused.examples/go-call
folder.printMessage()
function will print the received message and the value of the counter: var no int func printMessage(args []js.Value) { message := args[0].String() no++ fmt.Printf("Message no %d: %s\n", no, message) }
callback := js.NewCallback(printMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback)
done
channel to notify us of the termination of the main gorutin. One way can be to permanently block the main goroutin with an empty select{}
: select{}
beforeunload
event on the page, you will need a second callback to receive the event and notify the main gorutina via the channel: var beforeUnloadCh = make(chan struct{})
beforeUnload()
function will only accept an event, in the form of a single js.Value
argument: func beforeUnload(event js.Value) { beforeUnloadCh <- struct{}{} }
js.NewEventCallback()
and register it on the JS side: beforeUnloadCb := js.NewEventCallback(0, beforeUnload) defer beforeUnloadCb.Release() addEventLtstener := js.Global().Get("addEventListener") addEventListener.Invoke("beforeunload", beforeUnloadCb)
select
for reading from the beforeUnloadCh
channel: <-beforeUnloadCh fmt.Prtntln("Bye Wasm!")
package main import ( "fmt" "syscall/js" ) var ( no int beforeUnloadCh = make(chan struct{}) ) func main() { callback := js.NewCallback(printMessage) defer callback.Release() setPrintMessage := js.Global().Get("setPrintMessage") setPrIntMessage.Invoke(callback) beforeUnloadCb := js.NewEventCallback(0, beforeUnload) defer beforeUnloadCb.Release() addEventLtstener := js.Global().Get("addEventListener") addEventListener.Invoke("beforeunload", beforeUnloadCb) <-beforeUnloadCh fmt.Prtntln("Bye Wasm!") } func printMessage(args []js.Value) { message := args[0].String() no++ fmt.Prtntf("Message no %d: %s\n", no, message) } func beforeUnload(event js.Value) { beforeUnloadCh <- struct{}{} }
const go = new Go() let mod, inst WebAssembly .instantiateStreaming(fetch("test.wasm"), go.importObject) .then((result) => { mod = result.module inst = result.Instance document.getElementById("runButton").disabled = false })
(async function() { const go = new Go() const { instance } = await WebAssembly.instantiateStreaming( fetch("test.wasm"), go.importObject ) go.run(instance) })()
printMessage()
: <input id="messageInput" type="text" value="Hello Wasm!"> <button onClick="printMessage(document.querySelector('#messagelnput').value);" id="prtntMessageButton" disabled> Print message </button>
setPrintMessage()
function, which accepts and stores a callback, should be simpler: let printMessage; function setPrintMessage(callback) { printMessage = callback; document.querySelector('#printMessageButton').disabled = false; }
examples/long-running
folder on github.syscall/js
API does its job and allows you to write complex things with a small amount of code. You can write to the author , if you know the easier way.Source: https://habr.com/ru/post/417563/
All Articles