Gopher ante Web

Go im Browser: WebAssembly-Anwendungen in Go schreiben

Sebastian Holstein

© Shutterstock / Aleutie (wave / modifiziert),

© Reneé French (CC BY 3.0 DE), © SuS Media

 

Vor wenigen Wochen erschien die erste Betaversion von Go 1.11. Dies war ein ganz spezielles Release, denn es ist die erste Version der Programmiersprache mit initialer Unterstützung für WebAssembly (WASM). Eine gute Gelegenheit also, dich Go als Programmiersprache für WASM-Anwendungen anzusehen.

Dieser Artikel erschien im englischen Original auf dem Blog des Autors. Der Artikel kann hier gefunden werden.

Vorbereitung: Installation von Go 1.11 Beta

Bevor wir starten empfehle ich, eine stabile Version von Go zu installieren. Die neuesten Versionen findet ihr auf der Webseite von Go. Nachdem die aktuelle Version installiert und alles bereit ist, kann man mit den folgenden beiden Kommandos die Betaversion von Go 1.11 installieren:

go get golang.org/x/build/version/go1.11beta1
go1.11beta1 download

Anm. d. Redaktion: Mittlerweile ist bereits go1.11beta3 verfügbar!

Unsere erste Go/WASM-Anwendung

Nachdem die obigen Anweisungen zur Installation durchgeführt wurden, sollte es möglich sein, den Befehl go1.11beta1 im Terminal auszuführen. Dies ist das gewöhnliche go-Kommando für genau die Go-Version 1.11beta1 – ein nettes Feature, denn so können wir neue Go-Versionen einfach testen, ohne die auf dem System installierte stabile Go-Version zu überschreiben.

JavaScript Runtime

Um die Kommunikation zwischen Go und JavaScript herzustellen, müssen wir eine JavaScript-basierte Runtime hinzufügen. Die Betaversion von Go 1.11 hat eine Implementierung an Bord, mit der wir bestimmte JavaScript APIs aufrufen können. Die Implementierung kann im Verzeichnis der Go-Beta gefunden werden, in meinem Fall war sie unter:

~/sdk/go1.11beta1/misc/wasm

In diesem Verzeichnis finden sich neben der Runtime wasm_exec.js auch eine einfache HTML-Datei, die zeigt, wie die WASM-Datei geladen und ausgeführt wird. Ich habe den Code ein wenig modifiziert und ES2015-Modulimporte genutzt, das Beispiel kann in meinem Repository auf GitHub gefunden werden.

main.go

Nachdem wir das HTML und die Runtime aufgesetzt haben, können wir unsere erste Go-Anwendung für den Browser schreiben:

package main

import (
	"syscall/js"
	"time"
)

func main() {
	js.Global().Get("console").Call("log", "Hello world Go/wasm!")
	js.Global().Get("document").Call("getElementById", "app").Set("innerText", time.Now().String())
}

Global() stellt den Kontext zur Verfügung, den man auch beim Schreiben einer gewöhnlichen JavaScript-Anwendung für den Browser erwarten würde. Man hat Zugriff auf sämtliche globale Variablen, man kann Methoden aufrufen oder Attribute setzen. Wie man sieht: Alle diese Aktionen sind nicht typsicher.

Um nun eine WASM-Datei zu erstellen, muss das folgende Kommando ausgeführt werden:

GOARCH=wasm GOOS=js go1.11beta1 build -o app.wasm main.go

Unterstützung für WASM-MIME-Typen auf Web-Servern

Was ich bemerkt habe ist, dass viele Web-Server nicht den MIME-Type application/wasm unterstützt. Sollte der Web-Server dieses MIME-Type nicht senden, erlaubt der Browser die Ausführung der abgerufenen Datei. Stellt also in jedem Fall sicher, dass euer Web-Server den MIME-Type unterstützt.

In meinem Beispiel nutzte ich den fantastischen Caddy Web-Server (der ebenfalls auf Go basiert) und konfigurierte den MIME-Type im Caddyfile

localhost

mime {
  .wasm application/wasm
}

gzip {
 ext *
}

Beobachtungen

Hier möchte ich ein paar Beobachtungen teilen, die ich beim Nutzen von Go für WASM-Anwndungen gemacht habe:

1. WASM-Dateien werden risieg

Betrachten wir uns das folgende, simple Beispiel:

package main

import (
	"syscall/js"
)

func main() {
	js.Global().Get("console").Call("log", "Hello world Go/wasm!")
}

Beim ersten Kompilieren dieser Anwendungen zu WASM, war ich ein wenig geschockt, wie groß die WASM-Datei war: 1.3 MB für die Datei app.wasm (unkomprimiert) und 11.3 KB für die JavaScript Runtime:

Chrome Devtools network panel: 1.3 MB wasm file

Mit GZIP konnte ich das Ganze auf 295 KB komprimieren, was immer noch viel zu groß für eine Anwendung dieser Art ist! Doch warum ist die WASM-Datei so groß?

Da Go eine Sprache ist, die zu einem einzelnen, ausführbaren Binary kompiliert, liefert sie von Natur aus die Runtime mit jeder Go-Anwendung aus. Diese Runtime muss Teil der WASM-Datei sein, damit die Anwendung im Browser ausgeführt werden kann. Die Runtime nimmt den Löwenanteil der 1.3 MB der WASM-Datei ein.

Ein weiterer Grund ist das Fehlen von Tree-Shaking-Funktionen in Go. Die JavaScript Community hat im letzten Jahr hart daran gearbeitet, ungenutzten Code aus erstellten JavaScript Bundles herauszubekommen. Projekte wie Rollup oder Webpack haben das eindrucksvoll vorgemacht. Populäre Frontend Frameworks bzw. Librarys wie Angular oder React haben ihre Code-Basis geändert, um Code zu produzieren, der für Tree Shaking ein wenig besser geeignet ist. Dies führte zu deutlich kleineren Anwendungsgrößen.

Im Go-Ökosystem ist Tree Shaking nicht wirklich ein Thema, da die Größe von Anwendungen auf dem Server nicht so eine große Rolle spielt. Wenn man also jetzt ein Go-Package importiert, bekommt man das gesamte Package in einer WASM-Datei geliefert – unabhängig davon, wie viele Features des Pakets wirklich genutzt werden.

2. Keine Unterstützung für Threads

Keiner der großen Browser (mit Safari als Ausnahme) enthält die Unterstützung für Threads. Daran wird aktuell gearbeitet (wie man hier und hier sieht). Lädt man also eine WASM-Datei wie in meinem Beispiel, läuft sie ausschließlich auf dem Haupt-Thread und kann daher den Haupt-Thread wie JavaScript blockieren.

3. Keine Unterstützung von Entwickler-Tools für den Browser

Zu dem Zeitpunkt, da dieser Artikel entstand, gab es noch keinerlei Unterstützung von Entwickler-Tools für das Debugging und Inspecting von WebAssembly-Anwendungen im Browser. Es ist also am Entwickler selbst, eine Brücke zwischen JavaScript und der gewünschten Sprache zu schlagen, um grundlegende Logging-Funktionen im Browser zu erhalten. Aber auch daran arbeiten die Browser-Anbieter bereits.

It’s a wrap – Die Zusammenfassung

Ich hoffe, ich konnte euch einen ersten Eindruck davon vermitteln, wie man Go als WASM-Sprache nutzen kann. Eine Sache ist jedenfalls klar geworden: Dies ist erst der Beginn des Zusammenspiels von WASM und Go. Ich bin gespannt, welche Verbesserungen, Tools und Frameworks/Librarys wir in Zukunft nutzen und entdecken können.

Verwandte Themen:

Geschrieben von
Sebastian Holstein
Sebastian Holstein
Sebastian Holstein ist Software Architekt bei der adesso AG. Er beschäftigt sich mit neuen Webtechnologien, ist Fan der Programmiersprache Go und des Cloud Native Ökosystems.
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "Go im Browser: WebAssembly-Anwendungen in Go schreiben"

avatar
400
  Subscribe  
Benachrichtige mich zu:
fafi
Gast

Super Artikel – Gibt es für GoLang eine Roadmaps? Ist da tree-shaking in der nächsten Version eingeplant – dies fehlt damit der Artikel perfekt gewesen wäre 😉