Go and GopherJS
18 February 2015
Dmitri Shuralyov
Software Engineer, Triggit
Overview
- Go, and what if you could compile it to JavaScript.
- GopherJS can do that quite well.
- Demos and examples.
- Performance, code size, debugging story, etc.
2
Go
3
Motivation
- "Go is a general-purpose language for building simple, reliable, and fast software. It’s fun to write and a good fit for many use cases including web apps, network services, and command-line tools."
(From mmcgrana.github.io/2012/09/getting-started-with-go-on-heroku.html.)
- Great language to write libraries in, functionality becomes available everywhere, just go get and import.
4
Good for writing general code
Use packages to abstract out platform-specific implementation details:
path/filepath
os
, os/exec
net
, net/http
Use general interfaces that can be implemented and provided:
io.Reader
and io.Writer
vfs.FileSystem
:
type FileSystem interface {
Opener
Lstat(path string) (os.FileInfo, error)
Stat(path string) (os.FileInfo, error)
ReadDir(path string) ([]os.FileInfo, error)
String() string
}
5
Go target platforms
- OS X, Linux, Windows, arm (Raspberry Pi), arm64 (soon), Android (Go 1.4), iOS (Go 1.5~), others.
- More can be added in a coherent way.
- One thing missing?
6
GopherJS
- GopherJS - A compiler from Go to JavaScript.
- Its main purpose is to give you the opportunity to write front-end code in Go which will still run in all browsers.
- (There are/can be more than one Go -> JS compiler, just like there's
gc
and gccgo
. It's an implementation detail.)
7
GopherJS GitHub Repo
8
What is supported?
- Nearly everything. Including channels, goroutines, select.
- See compatibility table for list of supported packages (with passing tests).
- Compiler does some heavy lifting to support goroutines, which allows for normal (idiomatic style) Go code.
9
Demo.
Easy to get started.
$ go get -u github.com/gopherjs/gopherjs
$ gopherjs build
10
Reasons to use Go in frontend
- Like Node.js, share common code/logic between frontend and backend components.
- Familiar tools.
gofmt
, goimports
, godoc.org, `go test`, `go test -bench .`.
- Familiar types (int, uint16, []byte, string), no need for equality table, static type checking.
- Familiar compilation errors, refactoring.
- Familiar concurrency, goroutines, blocking receiving, instead of callbacks.
- Familiar libraries like
net/url
, time
, html/template
, third party ones like blackfriday
, etc.
- Ability to start from ground up with solid foundation and build high quality, sophisticated and complicated frontend UIs and projects.
11
Packages that can be compiled to JavaScript
go/parser
and go/printer
func process(input string) string {
// Parse the AST.
fset := token.NewFileSet()
fileAst, parseErr := parser.ParseFile(fset, "", input, parser.ParseComments|parser.AllErrors)
// Print the AST.
var config = &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
var buf bytes.Buffer
err := config.Fprint(&buf, fset, fileAst)
if err != nil {
panic(err)
}
// Append parsing errors, if any.
if parseErr != nil {
buf.WriteString("\n---\n" + parseErr.Error())
}
return buf.String()
}
12
Packages that can be compiled to JavaScript
github.com/russross/blackfriday
github.com/microcosm-cc/bluemonday
github.com/sourcegraph/syntaxhighlight
github.com/shurcooL/go/highlight_go
github.com/shurcooL/go/highlight_diff
go/format
github.com/shurcooL/markdownfmt/markdown
13
Packages that can be compiled to JavaScript
Achieving all that in the browser took minutes, because existing Go code could be reused:
import "github.com/shurcooL/go/github_flavored_markdown"
func run(event dom.Event) {
output.SetInnerHTML(string(github_flavored_markdown.Markdown([]byte(input.Value))))
}
func main() {
input.AddEventListener("input", false, run)
input.Value = initial
input.SelectionStart, input.SelectionEnd = len(initial), len(initial)
run(nil)
}
14
Running Go code in the browser?
- The browser is a strange execution environment.
- Even if you can compile Go to JavaScript, it takes getting used to (but it'll be insightful).
15
Running Go code in the browser?
- To do interesting things, you need to be able to have side effects (other than printing to console).
16
APIs in the browser
- DOM API
- XMLHttpRequest API
- WebSocket API
- WebRTC API
- WebGL API
- Geolocation API
- Gamepad API
- Notification API
- local file storage, full screen mode, mouse lock APIs
- ...
17
APIs in the browser
18
GopherJS and JavaScript
godoc.org/github.com/gopherjs/gopherjs/js
Accessing native JavaScript APIs in Go code:
// document.write("Hello world!");
js.Global.Get("document").Call("write", "Hello world!")
Providing Go functionality to other JavaScript code:
someGoFunc := func() {
...
}
js.Global.Set("SomeFunction", someGoFunc)
19
Type conversions between Go types and JavaScript types
godoc.org/github.com/gopherjs/gopherjs/js
20
APIs in the browser
21
APIs in the browser
Source: github.com/gopherjs/gopherjs/wiki/bindings
22
APIs in the browser
23
Bindings to JS libraries
24
Go Wrappers
25
GopherJS Issue Resolution Times
"Wow, awesome 1 hour fix, that was fast! Thanks!"
"Wow, that was fast - thank you very much for your efforts! :)"
"Great work! Thank you!"
"Thanks for your prompt replies!"
14 hours to fix a compiler bug.
26
GopherJS GitHub Repo
27
github.com/shurcooL/play/95
Minor change of topic...
Demo.
View Source: gotools.org/github.com/shurcooL/play/95
(Also github.com/shurcooL/play/97 and github.com/shurcooL/Hover.)
28
Other Demos
29
Compile Errors
30
Compile Errors
output, err := markdown.Process("", 12345, nil)
if err != nil {
panic(err)
}
31
Compile Errors
var _ io.Reader = markdownRenderer{}
32
Debugging
33
Debugging
- `fmt.Println("hello to println")`
- panic stack traces
- (static type checking, compiler errors eliminate a lot of problems)
34
Debugging
35
Debugging
36
Debugging
37
Debugging
38
Debugging
39
Performance
40
Performance
- Not faster than pure hand-written JavaScript
- Not much slower either, acceptable for most general use
- Often the slowest part is actual DOM manipulation, etc.
41
Performance
- Possible to benchmark via `gopherjs test -bench .`
- Possible to fallback to JavaScript as "assembly"; rewrite slow parts with careful hand-tuned JS
asm.js
support planned, not yet implemented
- (PNaCl, etc. might happen in the future, by 2050 browsers may simply support/run Go natively)
42
Performance
- Careful of more expensive string manipulation (Go uses utf-8, Unicode) if in a tight loop.
43
Size of generated code
- "Hello world" will be large due to fixed size overhead (Go/JS type conversions, etc.).
- Large program with same imports will be marginally larger.
- Extremely huge programs with huge recursive imports seem to max out at 200-350 KB.
44
Limitations
45
Limitations
- Need to mark blocking calls on interfaces as
//gopherjs:blocking
, or else. (Read full details here.)
- (Currently ongoing work to improve blocking detection, making that work unneeded.)
46
Limitations
- The need to mark
//gopherjs:blocking
prevents implementing blocking io.Reader
, net.Conn
interfaces (since you can't easily modify Go standard library to add those comments there).
- If a blocking
io.Reader
is supported, then it is possible to wrap a websocket connection in a way that implements net.Conn
interface. Doing that will allow using net/rpc/jsonrpc
package for RPC. It will also allow creating an http.Client
with a custom http.Transport
that wraps around xhr.
47
Limitations
- More steps to distribute apps that use GopherJS for frontend.
- `go generate` can be helpful in pre-compiling and bundling the generated js.
- Limited to one script per html page if you don't want to pay extra price for having two Go "runtimes".
- Still young and evolving, need to be able to adapt quickly, figure out and solve problems. Often travelling a path for the first time.
- (Minor improvement/API breaking change coming to use js.Object struct pointers rather than interface. See issue 174.)
- Less people familiar with Go than JavaScript, less existing "frameworks".
48
Advantages
- Get to use Go and its ecosystem (tools, websites, libraries, errors and type checking).
- Open ended possibilities. Just you, Go code, and whatever you want to create. As simple or sophisticated as you want.
- What would you rather invest into, and deal with 2 years from now? Imagine receiving pull requests, doing code review, maintaing and developing code further, etc.
49
Takeaway
Which language you use in the frontend is a choice you have to make.
50
Something fun to try at home
- Think of a neat general Go package (or multiple packages) you like, see if it can be compiled with GopherJS and used on a simple web page.
51
Community
github.com/gopherjs/gopherjs#community
- GitHub (repo, issues, organization)
- Google Group
- IRC channel #GopherJS
- Slack channel #GopherJS
- Follow twitter.com/GopherJS
52
Thank you
Dmitri Shuralyov
Software Engineer, Triggit
shurcooL@gmail.com
https://github.com/shurcooL
@shurcooL
53
Extras
54
Packages that can be compiled to JavaScript
go/parser
and go/printer
package main
import ("bytes"; "go/parser"; "go/printer"; "go/token"; "honnef.co/go/js/dom")
var document = dom.GetWindow().Document()
var input = document.GetElementByID("input").(*dom.HTMLTextAreaElement)
var output = document.GetElementByID("output").(dom.HTMLElement)
var initial = "package main\n\n..."
func run(_ dom.Event) {
output.SetTextContent(process(input.Value))
}
func main() {
input.AddEventListener("input", false, run)
input.Value = initial
input.SelectionStart, input.SelectionEnd = 153, 153
run(nil)
}
55
Code Samples
shareIcon := document.GetElementByID("share-icon")
shareIcon.AddEventListener("click", false, func(event dom.Event) {
event.PreventDefault()
fmt.Println("clicked!")
})
Setting CSS style of an element.
shareIcon.Style().SetProperty("display", "none", "")
56
Thank you
Use the left and right arrow keys or click the left and right
edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)