Туториал по Go и WebAssembly

Aus archium.org
Wechseln zu: Navigation, Suche


Назад

Inhaltsverzeichnis

Что такое WebAssembly?

Java-разработчики, независимо от аппаратного обеспечения, программируют виртуальную машину Java JVM, которая обрабатывает унифицированный байтовый код. Адаптация к соответствующей аппаратной архитектуре выполняется JVM, разработчику не нужно об этом беспокоиться (почти). Python использует аналогичный подход с своевременной компиляцией объектного кода. Эта идея доведена до совершенства с помощью LLVM — виртуальной машины низкого уровня, которая имитирует машину с программным обеспечением и поддерживает удивительно большое количество аппаратных архитектур. Для байт-кода LLVM есть компиляторы практически со всех современных языков — включая Java!

Представьте, что такой виртуальной машиной является веб-браузер — тогда вы поймёте, что такое WebAssembly (сокр. WASM).

WebAssembly — это стандарт байт-кода для управления функциями браузера. Даже если функциональность браузера в настоящее время все еще основана на JavaScript, с WebAssembly браузеры в долгосрочной перспективе станут независимыми от языков сценариев. Веб-проекты становятся свободными от кодов, которые прозрачны для всех. А вместе с этим и производительность веб-проектов значительно возрастет. Генерация байт-кода WebAssembly с помощью Go также значительно упрощает параллелизм, то есть параллельное выполнение подпрограмм.

WebAssembly — это долгожданное "недостающее звено" между веб-сайтом и браузером, которое ранее моделировалось только громоздкими и требовательными к памяти фреймворками JavaScript.

WebAssembly является официальным веб-стандартом с конца 2019 года. В ближайшие годы он приобретет огромное значение, многие веб-проекты придется заново реализовывать с использованием новых технологий. Производители браузеров выйдут на рынок виртуального оборудования со своими программными продуктами и вступят в гонку за самую производительную платформу и самые интересные функции! Например, уже давно существует Qt для WebAssembly. В ближайшем будущем WebAssembly превратит сайты в полноценные приложения!

Go и WebAssembly

Есть много способов работы с WebAssembly. Нам не нужно программировать на виртуальном ассемблере, уже есть компиляторы WebAssembly для многих языков программирования. Go поддерживает WASM с версии 1.11, то есть с лета 2018 года.

Несколько простых примеров

Для начала создайте 2 проекта:

  • simple WASM server
  • simple WASM app

Пример 1: "Hello World"

Шаг 1 [simple WASM server]: Создайте рабочий веб-сервер в файле "main.go"

package main

import (
	"log"
	"net/http"
)

const (
	addr = "localhost:8008"
	dir  = ""
)

func main() {
	log.Fatal(http.ListenAndServe(addr, http.FileServer(http.Dir(dir))))
}

Шаг 2 [simple WASM server]: Создайте файл "index.html"

<html>
	<head>
		<meta charset="utf-8"/>
	</head>
	<body>It works!</body>
</html>

Шаг 3: Протестируйте веб-сервер

go build -i -o simpleWASMserver main.go
./simpleWASMserver

В браузере введите адрес http://localhost:8008/. На сайте должно быть написано "It works!".

Шаг 4 [simple WASM server]: Измените файл index.html и подготовьтесь к вызову кода WebAssembly ("app.wasm").

Вариант а: для более новых браузеров, которые уже поддерживают WebAssembly.
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      const go = new Go();
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
  </head>
  <body></body>
</html>

Вариант b: с совместимостью для старых браузеров, которые еще не поддерживают потоковую передачу WebAssembly.
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
  </head>
  <body></body>
</html>

Шаг 5 [simple WASM app]: Создайте код WebAssembly в файле "main.go"

package main

import "fmt"

func main() {
	fmt.Println("Hello, WebAssembly!")
}

Шаг 6 [simple WASM app]: Скомпилируйте код WebAssembly

Внимание!
Для WebAssembly нам нужны измененные переменные среды
GOOS=js
GOARCH=wasm
GOOS=js GOARCH=wasm go build -o app.wasm
Внимание!
Файл с байт-кодом WebAssembly "app.wasm" необходимо скопировать в папку веб-сервера (simple WASM server)!

Шаг 7: Протестируйте новый сервер

Текст Hello, WebAssembly! теперь можно прочитать в консоли (Console-Log) веб-сервера.

Пример 2: Связь с объектной моделью документа (DOM) HTML-документа

Файл "index.html" [simple WASM server]

Идентичен варианту а или b из первого примера!

Файл "main.go" [simple WASM app]

package main

import "fmt"

func main() {
	fmt.Println("Hello, WebAssembly!")

	js.Global().Get("console").Call("log", "Just another way to send a string to console-log!")
	js.Global().Get("document").Call("getElementsByTagName", "body").Index(0).Set("innerText", time.Now().String())
}

Пример 3: Динамический контент

Файл "index.html" [simple WASM server]

https://golang.org/misc/wasm/wasm_exec.html, и соответственно https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.html были взяты как шаблон!

<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("convertButton").disabled = false;
        })
        .catch((err) => {
          console.error(err);
        });

      async function run() {
        console.clear();
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    </script>
  </head>
  <body>
    <input
      type="number"
      min="0"
      max="255"
      placeholder="0…255"
      size="5"
      id="decimal"
    />

    <button onClick="run();" id="convertButton" disabled>Convert</button><br />
    <div id="bin"></div>
    <div id="oct"></div>
    <div id="hex"></div>
  </body>
</html>

Файл "main.go" [simple WASM app]

package main

import (
	"fmt"
	"strconv"
	"syscall/js"
	"time"
)

func main() {
	var (
		inputString string
		inputInt    int
		err         error
	)

	inputString = js.Global().Get("document").Call("getElementById", "decimal").Get("value").String()
	if inputInt, err = strconv.Atoi(inputString); err != nil || inputInt < 0 || inputInt > 255 {
		inputInt = 0
	}

	js.Global().Get("document").Call("getElementById", "bin").Set("innerText", fmt.Sprintf("BIN: %08b\n", inputInt))
	time.Sleep(1 * time.Second) // Задержка добавлена для наглядности
	js.Global().Get("document").Call("getElementById", "oct").Set("innerText", fmt.Sprintf("OCT: %04o\n", inputInt))
	time.Sleep(1 * time.Second) // Задержка добавлена для наглядности
	js.Global().Get("document").Call("getElementById", "hex").Set("innerText", fmt.Sprintf("HEX: %02x\n", inputInt))
}

Протестируйте веб-сервер еще раз

Поле ввода теперь будет принимать число от 0 до 255 и преобразовывать его в двоичный, восьмеричный и шестнадцатеричный код.

Примечание
Задержка между вмешательствами в DOM HTML-документа предназначена только для иллюстративных целей.

Пример 4: Рисование на HTML5-Canvas

HTML5-Синтаксис для Canvas https://www.w3schools.com/graphics/canvas_drawing.asp.

На этот раз дополнительные элементы генерируются и удаляются в коде Go. Все, что осталось от исходного кода HTML5, — это <header>…</header> и пустой <body></body>.

Файл "index.html" [simple WASM server]

<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="https://golang.org/misc/wasm/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("app.wasm"), go.importObject)
        .then((result) => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("runButton").disabled = false;
        })
        .catch((err) => {
          console.error(err);
        });

      async function run() {
        console.clear();
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    </script>
  </head>
  <body>
    <button onClick="run();" id="runButton" disabled>run</button><br />
  </body>
</html>

Файл "main.go" [simple WASM app]

package main

import (
	"math/rand"
	"syscall/js"
	"time"
)

func main() {
	var (
		dombase, canvas, button, ctx, grd js.Value
	)
	dombase = js.Global().Get("document")

	button = dombase.Call("getElementById", "runButton")

	canvas = dombase.Call("createElement", "canvas")
	canvas.Set("width", 800)
	canvas.Set("height", 600)

	dombase.Call("getElementsByTagName", "body").Index(0).Call("insertBefore", canvas, button)

	dombase.Call("getElementsByTagName", "body").Index(0).Call("removeChild", button)

	ctx = canvas.Call("getContext", "2d")
	grd = ctx.Call("createLinearGradient", 0, 0, 800, 0)
	grd.Call("addColorStop", 0, "Crimson")
	grd.Call("addColorStop", 0.33, "Orange")
	grd.Call("addColorStop", 0.66, "SeaGreen")
	grd.Call("addColorStop", 1, "DarkTurquoise")
	ctx.Set("fillStyle", grd)
	ctx.Call("fillRect", 0, 0, 800, 600)

	ctx.Call("moveTo", 0, 0)
	for i := 0; i < 799; i++ {
		x := rand.Intn(100)
		y := rand.Intn(100)

		ctx.Call("lineTo", i+x, (i*2/3)+y)
		ctx.Call("stroke")
		ctx.Set("strokeStyle", "white")
		time.Sleep(50 * time.Millisecond)
	}
}

Протестируйте веб-сервер еще раз

Результат должен выглядеть примерно так: ArchiumWASM Demo Canvas.png

Пример 5: Вставка большого количества кода HTML/CSS/JS

Файл "index.html" [simple WASM server]

Идентичен варианту а или b из первого примера!

Файл "main.go" [simple WASM app]

package main

import (
	"syscall/js"
)

func main() {
	var (
		dombase, body js.Value
	)
	dombase = js.Global().Get("document")
	body = dombase.Call("getElementsByTagName", "body").Index(0)

	body.Set("innerHTML", `
	<table style="text-align:center;">
	<tr><td colspan="2">SVG</td></tr>
	<tr><td>Polygon</td><td>Path</td>
	<tr><td><svg height="210" width="500">
  	<polygon points="200,10 250,190 160,210" style="fill:lime;stroke:purple;stroke-width:1" />
	</svg></td><td>
	 <svg height="400" width="450">
  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
  stroke-width="3" fill="none" />
  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
  stroke-width="3" fill="none" />
  <path d="M 175 200 l 150 0" stroke="green" stroke-width="3"
  fill="none" />
  <path d="M 100 350 q 150 -300 300 0" stroke="blue"
  stroke-width="5" fill="none" />
  <!-- Mark relevant points -->
  <g stroke="black" stroke-width="3" fill="black">
    <circle id="pointA" cx="100" cy="350" r="3" />
    <circle id="pointB" cx="250" cy="50" r="3" />
    <circle id="pointC" cx="400" cy="350" r="3" />
  </g>
  <!-- Label the points -->
  <g font-size="30" font-family="sans-serif" fill="black" stroke="none"
  text-anchor="middle">
    <text x="100" y="350" dx="-30">A</text>
    <text x="250" y="50" dy="-10">B</text>
    <text x="400" y="350" dx="30">C</text>
  </g>
</svg></td></tr>
	</table>`) //https://www.w3schools.com/graphics/svg_polygon.asp & https://www.w3schools.com/graphics/svg_path.asp
}

Пример 6: Загрузка изображений из кода Go

Файл "index.html" [simple WASM server]

Идентичен варианту а или b из первого примера!

Файл "main.go" [simple WASM app]

package main

import (
	"syscall/js"
)

func main() {
	const imageByteArrayAsBase64 = `
iVBORw0KGgoAAAANSUhEUgAAAJgAAAC0CAMAAABfch
VeAAAC/VBMVEUGAwkBAg8ABgkEAAYLBAIIBQsCCQwICwYMCQ4GDA8QCggTCgEVDg
ULERMPEQ0SDxMUDw4aEgMQFBYTFBIWGBUgFwYbGBcaGBsaHBknGwUgHiEeIB0iHh
0tHgMiJCEvIgkoJCMnJSg1JQYmKCU8KgcrLSovKyo+KBMrLS87LBBBLgU3LxtHLg
gwMzUyNDE3MzJMMgVJNwk4OTdMOQJSNgM4Oz0+Ojk8PDVYNwdVOQdSPAg+QD1ePA
RDPz9YQgVTQRpBQ0JeQAZlQgJGR0VuRAdrRgdKS0loSwl0SQNUTz5yTAJTTk1NUV
NPUU5uUQV7TwBVV1RaV0qAUwV9VwaFVgCLVgNhXlFdXlxsXUNdYGOLWwWBXhGGYQ
aPXgCTXABlZmSZYQSWZASPaQqVaA6baAxsbmyhaAGMayuWbgCPcBKbbQKfawN0cW
SYbCGkbwlzdXKbdQmscACocwCidA2GeGJ6e3ilewOhfQSeeSezdgF4fX+veAmsew
qyewCKf1x+gH2ifDiygAW3fgSthQO1fhOZgVKEhYOygiC8gw2+hQC4hRK7hwDDiQ
WMjou4kQSli1u/kAPCjQ3HjA3KjgC7jyrHkQC5jjnNkQKUlpO1k0/UkQrJmAfRlA
rOlwvWmADFoQDKngXTmwCvmm7GmjDRngCdn5zXngTbnAWinZyunYC3nmTaoQ3Vni
7OqA7VpQzepADApGXUrQDipwOnqKXbrQTZpyvgqwfmqw3qrQDisgDnsACur6y7rZ
TZtxHrtAPfuwHwsgbkugW9sp+7s6bquQrVt071twDvuA7ztg/pvgDuvADzugC2uL
S8trPxvgDpwwD2vQDLuonuwgDuvSb0wQO7vbrBvLruyAjDv7HwxB3rywvqv1nAwr
/Gwb/1zgHtzSTFx8TKxcTx0Rr10wXsx2bMybrJy8j02A/x1yD01zDO0M312DvV0c
P22EbT1dHl17Hc2MnY2tfk4NHe4d3x4rnj5eLo6ubr7ur18eLw8u/19/P5+/f6/P
n7/fr8//sGU4jmAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx
MAAAsTAQCanBgAAAAHdElNRQfUChwRIgJBXOpUAAAcEUlEQVR42u2cfVzT953A2y
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
`
	var (
		dombase, body js.Value
	)

	dombase = js.Global().Get("document")
	body = dombase.Call("getElementsByTagName", "body").Index(0)

	image := dombase.Call("createElement", "img")
	image.Set("src", "data:image/png;base64,"+imageByteArrayAsBase64)
	body.Call("appendChild", image)
}

Протестируйте веб-сервер еще раз

Результат должен выглядеть так:
ArchiumWASM Demo Tux.png

Совет: создавайте код полностью на Go

Файл "index.html" использовался в примерах для упрощения. HTML-код также можно сгенерировать полностью с помощью Go.
Файл "https://golang.org/misc/files/wasm_exec.js" также хранится локально:

[simple WASM server]: Файл "main.go" без "index.html"

package main

import (
	"fmt"
	"log"
	"net/http"
)

const (
	addr = "localhost:8008"
	dir  = ""
)

func myDefaultHandlerFunc(w http.ResponseWriter, r *http.Request, dirpath string) {
	fmt.Fprintf(w, `<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
    <script src="/files/wasm_exec.js"></script>
    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();
      let mod, inst;
      WebAssembly.instantiateStreaming(fetch("/files/app.wasm"), go.importObject)
        .then((result) => {
          go.run(result.instance);
        })
        .catch((err) => {
          console.error(err);
        });
    </script>
</head>
<body></body>
</html>`)
}

func main() {
	http.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir(dir))))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { myDefaultHandlerFunc(w, r, dir) })

	log.Fatal(http.ListenAndServe(addr, nil))
}

Ссылки (Eng)