GopherJS - A compiler from Go to JavaScript
GopherJS compiles Go code (golang.org) to pure JavaScript code. Its main purpose is to give you the opportunity to write front-end code in Go which will still run in all browsers. Give GopherJS a try on the GopherJS Playground.
What is supported?
Nearly everything, including Goroutines (compatibility table). Performance is quite good in most cases, see HTML5 game engine benchmark.
Installation and Usage
Get or update GopherJS and dependencies with:
go get -u github.com/gopherjs/gopherjs
Now you can use gopherjs build [package]
, gopherjs build [files]
or gopherjs install [package]
which behave similar to the go
tool. For main
packages, these commands create a .js
file and .js.map
source map in the current directory or in $GOPATH/bin
. The generated JavaScript file can be used as usual in a website. Use gopherjs help [command]
to get a list of possible command line flags, e.g. for minification and automatically watching for changes.
If you want to use gopherjs run
or gopherjs test
to run the generated code locally, install Node.js 4.x and the module source-map-support
:
npm install --global source-map-support
For system calls (file system access, etc.), see this page.
Note: GopherJS will try to write compiled object files of the core packages to your $GOROOT/pkg directory. If that fails, it will fall back to $GOPATH/pkg.
Getting started
Interacting with the DOM
The package github.com/gopherjs/gopherjs/js
(see documentation) provides functions for interacting with native JavaScript APIs. For example the line
document.write("Hello world!");
would look like this in Go:
js.Global.Get("document").Call("write", "Hello world!")
You may also want use the DOM bindings, the jQuery bindings (see TodoMVC Example) or the AngularJS bindings. Those are some of the bindings to JavaScript APIs and libraries by community members.
Providing library functions for use in other JavaScript code
Set a global variable to a map that contains the functions:
package main
import "github.com/gopherjs/gopherjs/js"
func main() {
js.Global.Set("pet", map[string]interface{}{
"New": New,
})
}
type Pet struct {
name string
}
func New(name string) *js.Object {
return js.MakeWrapper(&Pet{name})
}
func (p *Pet) Name() string {
return p.name
}
func (p *Pet) SetName(name string) {
p.name = name
}
For more details see Jason Stone's blog post about GopherJS.
Architecture
General
GopherJS emulates a 32-bit environment. This means that int
, uint
and uintptr
have a precision of 32 bits. However, the explicit 64-bit integer types int64
and uint64
are supported. The GOARCH
value of GopherJS is "js". You may use it as a build constraint: // build js
.
Application Lifecycle
The main
function is executed as usual after all init
functions have run. JavaScript callbacks can also invoke Go functions, even after the main
function has exited. Therefore the end of the main
function should not be regarded as the end of the application and does not end the execution of other goroutines.
In the browser, calling os.Exit
(e.g. indirectly by log.Fatal
) also does not terminate the execution of the program. For convenience, it calls runtime.Goexit
to immediately terminate the calling goroutine.
Goroutines
Goroutines are fully supported by GopherJS. The only restriction is that you need to start a new goroutine if you want to use blocking code called from external JavaScript:
js.Global.Get("myButton").Call("addEventListener", "click", func() {
go func() {
[...]
someBlockingFunction()
[...]
}()
})
How it works:
JavaScript has no concept of concurrency (except web workers, but those are too strictly separated to be used for goroutines). Because of that, instructions in JavaScript are never blocking. A blocking call would effectively freeze the responsiveness of your web page, so calls with callback arguments are used instead.
GopherJS does some heavy lifting to work around this restriction: Whenever an instruction is blocking (e.g. communicating with a channel that isn't ready), the whole stack will unwind (= all functions return) and the goroutine will be put to sleep. Then another goroutine which is ready to resume gets picked and its stack with all local variables will be restored. This is done by preserving each stack frame inside a closure.