Einleitung: Warum Go und WebAssembly auf fortgeschrittenem Niveau?
\n\nWebAssembly (Wasm) hat sich von einer experimentellen Technologie zu einem Standard für leistungsstarke Webanwendungen entwickelt. Go, mit seiner effizienten Kompilierung und integrierten Wasm-Unterstützung, ist eine der besten Sprachen für die Erstellung von Modulen, die an den Grenzen der Browser-Fähigkeiten arbeiten. Die grundlegenden "Hello, World!"-Beispiele sind nicht mehr relevant – moderne Aufgaben erfordern eine feine Speicherverwaltung, einen bidirektionalen Datenaustausch mit JavaScript und die Optimierung der Binärgröße.
\n\nIn diesem Artikel behandeln wir fortgeschrittene Techniken für die Arbeit mit WebAssembly in Go. Sie erfahren, wie Sie komplexe Datenstrukturen übergeben, Go-Funktionen aus JavaScript aufrufen und umgekehrt, sowie die Leistung Ihrer Wasm-Module optimieren. Das Material richtet sich an Entwickler, die bereits mit den Grundlagen von Go vertraut sind und ihre Fähigkeiten auf die nächste Stufe heben möchten.
\n\n1. Speicherverwaltung: Übergabe von Strings, Arrays und Strukturen
\n\nEines der Hauptprobleme bei der Arbeit mit WebAssembly ist die Übergabe komplexer Daten zwischen dem linearen Speicher von Wasm und dem Heap von JavaScript. Go verwendet einen eigenen Garbage Collector, was Einschränkungen mit sich bringt. Betrachten wir fortgeschrittene Methoden.
\n\n1.1. Kopieren von Strings mit `copyBytesToGo` und `copyBytesToJS`
\n\nDie direkte Übergabe von Strings über Funktionsparameter ist für große Datenmengen ineffizient. Verwenden Sie stattdessen die Methoden des Pakets syscall/js:
package main\n\nimport (\n "syscall/js"\n "unsafe"\n)\n\n//export processLargeString\nfunc processLargeString(this js.Value, args []js.Value) interface{} {\n if len(args) < 1 {\n return nil\n }\n jsStr := args[0]\n // Länge des Strings ermitteln\n length := jsStr.Get("length").Int()\n // Puffer im Go-Speicher erstellen\n buf := make([]byte, length)\n // Daten aus JS nach Go kopieren\n js.CopyBytesToGo(buf, jsStr)\n // Jetzt enthält buf die UTF-8-Bytes des Strings\n // ... Verarbeitung ...\n // Ergebnis zurückgeben\n result := []byte("Antwort: " + string(buf))\n dst := js.Global().Get("Uint8Array").New(len(result))\n js.CopyBytesToJS(dst, result)\n return dst\n}\n\nfunc main() {\n c := make(chan struct{}, 0)\n js.Global().Set("processLargeString", js.FuncOf(processLargeString))\n <-c\n}\n\n\n1.2. Übergabe von Strukturen mittels JSON-Serialisierung
\n\nFür komplexe verschachtelte Strukturen verwenden Sie JSON als Zwischenformat. Dies ist langsamer als die direkte Arbeit mit dem Speicher, aber deutlich bequemer:
\n\n//export processUserData\nfunc processUserData(this js.Value, args []js.Value) interface{} {\n if len(args) < 1 {\n return js.ValueOf(map[string]interface{}{"error": "keine Daten"})\n }\n // JSON-String aus JS abrufen\n jsonStr := args[0].String()\n var user User\n if err := json.Unmarshal([]byte(jsonStr), &user); err != nil {\n return js.ValueOf(map[string]interface{}{"error": err.Error()})\n }\n // Daten verarbeiten\n user.Age++\n // JSON zurückgeben\n result, _ := json.Marshal(user)\n return js.ValueOf(string(result))\n}\n\n\n2. Bidirektionale Kommunikation: JavaScript aus Go aufrufen und umgekehrt
\n\nFortgeschrittene Integration erfordert, dass das Go-Modul nicht nur auf Aufrufe antworten, sondern auch Aktionen in der JavaScript-Umgebung initiieren kann.
\n\n2.1. Registrierung von Go-Callbacks für JavaScript
\n\nVerwenden Sie js.FuncOf, um Funktionen zu erstellen, die aus JS sichtbar sind. Es ist wichtig, deren Lebenszyklus korrekt zu verwalten:
var callback js.Func\n\nfunc registerCallback() {\n callback = js.FuncOf(func(this js.Value, args []js.Value) interface{} {\n // Callback aus JS abrufen\n jsCallback := args[0]\n // Mit Daten aus Go aufrufen\n go func() {\n // Asynchrone Arbeit emulieren\n time.Sleep(100 * time.Millisecond)\n jsCallback.Invoke(js.ValueOf("Ergebnis aus Go"))\n }()\