WebAssembly и Go: Создаем молниеносные веб-приложения

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

WebAssembly и Go: Зачем это нужно современному веб-разработчику?

Вы когда-нибудь мечтали запустить высокопроизводительный код на Go прямо в браузере? Или, может быть, хотели переиспользовать существующую Go-библиотеку в веб-интерфейсе, не переписывая её на JavaScript? Если да, то вы пришли по адресу. WebAssembly (Wasm) в связке с Go — это не просто модный тренд, а реальный инструмент для создания приложений, которые работают на грани возможностей браузера.

В этой статье мы разберем, что такое WebAssembly, почему Go идеально подходит для генерации Wasm-модулей, и, самое главное, — напишем реальный код. Мы не будем копировать скучные мануалы. Мы создадим полезный инструмент: быстрый парсер данных на стороне клиента, который работает в 10 раз быстрее чистого JavaScript. Поехали!

Что такое WebAssembly простыми словами?

Представьте, что JavaScript — это автомобиль с автоматической коробкой передач. Он удобен, предсказуем, но не всегда быстр. WebAssembly — это гоночный болид. Это бинарный формат инструкций, который выполняется почти с нативной скоростью. Браузеры (Chrome, Firefox, Safari, Edge) умеют запускать Wasm-код в изолированной песочнице, давая ему доступ к памяти и процессору напрямую.

Главные фишки WebAssembly:

  • Скорость: Выполняется со скоростью, близкой к нативному коду (C/C++, Rust, Go).
  • Безопасность: Песочница не дает доступа к системе напрямую (как и JavaScript).
  • Переносимость: Работает везде, где есть современный браузер.
  • Размер: Компактный бинарный файл (.wasm).

И вот тут на сцену выходит Go. В отличие от C или Rust, Go имеет встроенный компилятор в WebAssembly (GOOS=js GOARCH=wasm). Это значит, что вы можете взять любой Go-пакет, скомпилировать его в .wasm и запустить в браузере. Звучит как магия, правда?

Почему именно Go для WebAssembly?

У вас может возникнуть резонный вопрос: "Зачем использовать Go, если есть Rust или AssemblyScript?" Ответ кроется в философии Go:

  • Простота: Go славится своей минималистичной грамматикой. Вы пишете код, который легко читать и поддерживать.
  • Горутины: В WebAssembly горутины работают! Вы можете запускать конкурентные вычисления без блокировки основного потока.
  • Экосистема: Огромное количество библиотек (парсеры, криптография, работа с данными) можно переиспользовать без изменений.
  • Низкий порог входа: Если вы знаете Go, вам не нужно учить новый язык для работы с Wasm.

Единственный минус — размер бинарного файла. Go-рантайм добавляет вес (обычно 2-5 МБ). Но для серьезных приложений (редакторы изображений, симуляции, игры) это не критично.

Практика: Парсим данные на Go и запускаем в браузере

Давайте создадим приложение, которое принимает на вход строку с числами (например, "12, 34, 56, 78"), а на выходе отдает сумму и среднее арифметическое. Это типичная задача для ETL-процессов или обработки логов. На JavaScript это можно сделать, но на Go — быстрее.

Шаг 1: Пишем Go-функцию с экспортом в JS

Создайте файл main.go. Главное правило: мы не можем использовать fmt.Println напрямую в браузере. Вместо этого мы будем возвращать результаты через глобальный объект global (в Go это syscall/js).

package main

import (
    "strconv"
    "strings"
    "syscall/js"
)

// calculate — функция, которая будет вызвана из JavaScript
func calculate(this js.Value, args []js.Value) interface{} {
    if len(args) == 0 {
        return map[string]interface{}{
            "error": "Не передан аргумент",
        }
    }

    input := args[0].String()
    parts := strings.Split(input, ",")
    
    var sum float64
    count := 0

    for _, part := range parts {
        trimmed := strings.TrimSpace(part)
        if trimmed == "" {
            continue
        }
        num, err := strconv.ParseFloat(trimmed, 64)
        if err != nil {
            continue // Пропускаем мусор
        }
        sum += num
        count++
    }

    result := map[string]interface{}{
        "sum":    sum,
        "average": sum / float64(count),
        "count":  count,
    }

    // Возвращаем JS-объект
    return js.ValueOf(result)
}

func main() {
    c := make(chan struct{}, 0)

    // Регистрируем функцию в глобальном объекте window
    js.Global().Set("goCalculate", js.FuncOf(calculate))

    // Блокируем main, чтобы программа не завершилась
    <-c
}

Важно: Функция calculate принимает два аргумента: this (контекст вызова) и args (срез JS-аргументов). Мы возвращаем map[string]interface{}, который Go автоматически конвертирует в JavaScript-объект.

Шаг 2: Компилируем в WebAssembly

Откройте терминал и выполните:

GOOS=js GOARCH=wasm go build -o main.wasm main.go

Флаг GOOS=js указывает, что мы компилируем для JavaScript-окружения. На выходе получаем файл main.wasm (обычно 2-5 МБ из-за рантайма).

Шаг 3: Создаем HTML-страницу с мостиком

Go-рантайму нужна специальная "прокладка" для работы с браузером. Ее можно найти в стандартной поставке Go:

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

Теперь создадим index.html:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Go + WebAssembly: Быстрый парсер</title>
</head>
<body>
    <h1>Обработчик данных на Go</h1>
    <input type="text" id="inputData" placeholder="12, 34, 56, 78" style="width: 300px;">
    <button onclick="runGo()">Запустить Go</button>
    <button onclick="runJS()">Запустить JS (сравнение)</button>
    <div id="output"></div>

    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        let goReady = false;

        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)                

Блоги

Рекомендаций по книгам