Благодаря WebAssembly можно писать Frontend на Go +13


Оригинал статьи.


В феврале 2017 года член команды go Brad Fitzpatrick предложил сделать поддержку WebAssembly в языке. Спустя четыре месяца в ноябре 2017 автор GopherJS Ричард Музиол начал реализовывать идею. И, наконец, полная реализация была смержена в mаster. Разработчики получат wasm примерно в августе 2018, с версией go 1.11. В результате, стандартная библиотека берёт на себя почти все технические сложности с импортом и экспортом функций, знакомых вам, если вы уже пробовали компилировать Си в wasm. Звучит многообещающе. Давайте посмотрим, что можно сделать с первой версией.



Все примеры в этой статье, могут быть запущены из docker контейнеров, что лежат в репозитории автора:

?docker container run -dP nlepage/golang_wasm:examples
# Find out which host port is used
docker container ls

Затем перейдите на localhost:32XXX/, и переходите от одной ссылке к другой.

Привет, Wasm!


Создание базового «hello world» и концепции уже довольно хорошо задокументированы (даже на русском), поэтому давайте просто побыстее перейдём к более тонким вещам.

Самое необходимое — свежескомпилированная версия Go, поддерживающая wasm. Я не буду пошагово описывать установку, просто знайте, что необходимое уже в master.

Если вы не хотите беспокоиться об этом, Dockerfile c go доступен в репозитории golub-wasm на github, или ещё быстрее можно взять образ из nlepage/golang_wasm.

Теперь вы можете написать традиционный helloworld.go и скомпилировать его с помощью следующей команды:

?GOOS=js GOARCH=wasm go build -o test.wasm helioworld.go

В образе nlepage/golang_wasm уже установлены переменные окружения GOOS и GOARCH, поэтому можно использовать файл Dockerfile, подобный этому, для компиляции:

FROM nlepage/golang_wasm
COPY helloworld.go /go/src/hello/
RUN go build -o test.wasm hello

Последний шаг заключается в использовании файлов wasm_exec.html и wasm_exec.js, доступных в репозитории go в каталоге misc/wasm или в docker образе nlepage/golang_wasm в каталоге /usr/local/go/misc/wasm/, для выполнения test.wasm в браузере (wasm_exec.js ожидает двоичный файл test.wasm, поэтому используем это имя).
Вам просто нужно отдавать 3 статических файла, используя nginx, например, тогда wasm_exec.html отобразит кнопку «run» (включится, только если test.wasm загружен правильно).

Примечательно, что test.wasm необходимо обслуживать с MIME типом application/wasm, иначе браузер откажется от его исполнения. (например, nginx нуждается в обновленном файле mime.types).

Вы можете использовать образ nginx из nlepage/golang_wasm, который уже включает исправленный MIME тип, wasm_exec.html и wasm_exec.js в каталоге code>/usr/share/nginx/html/.

Теперь нажмите кнопку «run», затем откройте консоль своего браузера, и вы увидите приветствие console.log(«Hello Wasm!»).


Полный пример доступен тут.

Вызов JS из Go


Теперь, когда успешно запустили первый двоичный файл WebAssembly, скомпилированный из Go, давайте немного подробнее рассмотрим предоставляемые возможности.

Новый пакет syscall/js внесён в стандартную библиотеку, рассмотрим главный файл — js.go.
Доступен новый тип js.Value, который представляет значение JavaScript.

Он предлагает простой API для управления JavaScript переменными:

  • js.Value.Get() и js.Value.Set() возвращают и устанавливают значения полей объекта.
  • js.Value.Index() и js.Value.SetIndex() обращаются к объекту по индексу на чтение и запись.
  • js.Value.Call() вызывает метод объекта как функцию.
  • js.Value.Invoke() вызывает сам объект как функцию.
  • js.Value.New() вызывает оператор new и использует собственное знаяение как конструктор.
  • Еще несколько методов для получения значения JavaScript в соответствующем типе Go, например js.Value.Int() или js.Value.Bool().

И дополнительные интересные методы:

  • js.Undefined() даст js.Value соответствующий undefined.
  • js.Null() даст js.Value соответствующий null.
  • js.Global() вернёт js.Value, дающее доступ к глобальной области видимости.
  • js.ValueOf() принимает примитивные типы Go и возвращают корректное js.Value

Вместо вывода сообщения в os.StdOut, давайте отобразим его в окне оповещения с помощью window.alert().

Поскольку находимся в браузере, глобальная область видимости — окно, поэтому сначала надо получить alert() из глобальной области:

alert := js.Global().Get("alert")

Теперь у нас есть переменная alert, в виде js.Value, которая является ссылкой на window.alert JS, и можно использовать вызвать функцию через js.Value.Invoke():

alert.Invoke("Hello wasm!")

Как можно увидеть, нет необходимости вызывать js.ValueOf() перед передачей аргументов Invoke, он принимает произвольное количество interface{} и пропускает значения через ValueOf самостоятельно.

Теперь наша новая программа должна выглядеть так:

package main

import (
    "syscall/js"
)

func main() {
    alert := js.Global().Get("alert")
    alert.Invoke("Hello Wasm!")
}

Как и в первом примере, просто нужно создать файл с именем test.wasm, и оставить wasm_exec.html и wasm_exec.js как было.
Теперь, когда нажимаем кнопку «Run», появляется alert окно с нашим сообщением.

Рабочий пример есть в папкеexamples/js-call.

Вызов Go из JS.


Вызов JS из Go довольно прост, давайте рассмотрим внимательнее пакет syscall/js, второй файл для просмотра — callback.go.

  • js.Callback тип-обёртка для функции Go, для использования в JS.
  • js.NewCallback() функция, которая принимает функцию (принимающую срез js.Value и ничего не возвращающую), и возвращает js.Callback.
  • Некоторая механика для управления активными обратными вызовами и js.Callback.Release(), который должен вызываться для уничтожения обратного вызова.
  • js.NewEventCallback() аналогично js.NewCallback(), но оборачиваемая функция принимает только 1 аргумент — событие.

Давайте попробуем сделать что-то простое: запустить Go fmt.Println() со стороны JS.

Внесём некоторые изменения в wasm_exec.html, что бы иметь возможность получить обратный вызов от Go, чтобы вызвать его.

async function run() {
    console.clear();
    await go.run(inst);
    inst = await WebAssembly.instantiate(mod, go.ImportObject); // сброс экземпляра
}

Это запускает двоичный файл wasm и ждет его завершения, затем повторно инициализирует его для следующего запуска.

Давайте добавим новую функцию, которая получит и сохранит обратный вызов Go и изменит состояние Promise по завершению:

let printMessage // Our reference to the Go callback
let printMessageReceived // Our promise
let resolvePrintMessageReceived // Our promise resolver 
function setPrintMessage(callback) { 
    printMessage = callback 
    resolvePrintMessageReceived()
}

Теперь давайте адаптируем функцию run() для использования обратного вызова:

async function run() {
    console.clear()
    // Create the Promise and store its resolve function 
    printMessageReceived = new Promise(resolve => { 
        resolvePrintMessageReceived = resolve
    })
    const run = go.run(inst) // Start the wasm binary
    await printMessageReceived // Wait for the callback reception 
    printMessage('Hello Wasm!') // Invoke the callback 
    await run // Wait for the binary to terminate
    inst = await WebAssembly.instantiate(mod, go.importObject) // reset instance
}

И это на стороне JS!

Теперь в части Go нужно создать обратный вызов, отправить его на сторону JS и ожидать, когда функция понадобится.

    var done = make(chan struct{})

Затем должны написать настоящую функцию printMessage():

func printMessage(args []js.Value) {
    message := args[0].Strlng()
    fmt.Println(message)
    done <- struct{}{} // Notify printMessage has been called
}

Аргументы переданы через срез []js.Value, поэтому нужно вызвать js.Value.String() в первом элементе среза, чтобы получить сообщение в строке Go.
Теперь можем обернуть эту функцию в обратный вызов:

callback := js.NewCallback(printMessage)
defer callback.Release() // to defer the callback releasing is a good practice   

Затем вызовите функцию JS setPrintMessage(), точно так же, как при вызове window.alert():

setPrintMessage := js.Global.Get("setPrintMessage")
setPrintMessage.Invoke(callback)

Последнее, что нужно сделать, это дождаться вызова callback в main:

<-done

Эта последняя часть важна, потому что обратные вызовы выполняются в выделенной goroutine, и основная goroutine должна ждать вызова callback'а, иначе двоичный файл wasm будет остановлен преждевременно.

Полученная в результате программа Go должна выглядеть так:

?package main

import (
    "fmt"
    "syscall/js"
)

var done = make(chan struct{})

func main() {
    callback := js.NewCallback(prtntMessage)
    defer callback.Release()
    setPrintMessage := js.Global().Get("setPrintMessage")
    setPrIntMessage.Invoke(callback)
    <-done
}

func printMessage(args []js.Value) {
    message := args[0].Strlng()
    fmt.PrintIn(message)
    done <- struct{}{}
}

Как в предыдущих примерах создадим файл с именем test.wasm. Также нужно заменить wasm_exec.html на нашу версию, а wasm_exec.js сможем использовать повторно.

Теперь, при надатии кнопки «run», как в нашем первом примере, сообщение печатается в консоли браузера, но на этот раз это намного лучше! (И сложнее.)

Рабочий пример в биде docker файла доступен в папке examples/go-call.

Долгая работа


Вызов Go from JS является немного более громоздким, чем вызов JS от Go, особенно на стороне JS.

Это в основном связано с тем, что нужно дождаться, когда результат обратного вызова Go будет передан стороне JS.

Давайте попробуем что-то другое: почему бы не организовать двоичный файл wasm, который не завершится сразу после вызова callback, а будет продолжать работать и принимать другие вызовы.
На этот раз давайте начнем со стороны Go, и как в нашем предыдущем примере, нужно создать обратный вызов и отправить его стороне JS.

Добавим счетчик вызовов, чтобы отслеживать, сколько раз была вызвана функция.

Наша новая функция printMessage() будет печатать полученное сообщение и значение счетчика:

?var no int

func printMessage(args []js.Value) { 
    message := args[0].String() 
    no++
    fmt.Printf("Message no %d: %s\n", no, message)
}

Создание обратного вызова и отправка его на сторону JS такое же, как в предыдущем примере:

?callback := js.NewCallback(printMessage)
defer callback.Release()
setPrintMessage := js.Global().Get("setPrintMessage")
setPrIntMessage.Invoke(callback)

Но на этот раз у нас нет канала done, чтобы уведомить нас о прекращении основной горутин. Один из способов может заключаться в том, чтобы навсегда заблокировать главную goroutin'у пустым select{}:

select{}

Это не удовлетворительно, наш двоичный wasm будет просто висеть в памяти до закрытия вкладки браузера.

Можно прослушивать событие beforeunload на странице, понадобится второй обратный вызов для получения события и уведомления главной горутины по каналу:

var beforeUnloadCh = make(chan struct{})

На этот раз новая функция beforeUnload() будет принимать только событие, в виде единственного js.Value аргумента:

func beforeUnload(event js.Value) {
    beforeUnloadCh <- struct{}{}
}

Затем обернём его в обратный вызов с помощью js.NewEventCallback() и зарегистрируем на стороне JS:

beforeUnloadCb := js.NewEventCallback(0, beforeUnload)
defer beforeUnloadCb.Release()
addEventLtstener := js.Global().Get("addEventListener")
addEventListener.Invoke("beforeunload", beforeUnloadCb)

Наконец, заменим пустой, блокирующий select на чтение из канала beforeUnloadCh:

<-beforeUnloadCh
fmt.Prtntln("Bye Wasm!")

Финальная программа выглядит так:

?package main

import (
    "fmt"
    "syscall/js"
)

var (
    no             int
    beforeUnloadCh = make(chan struct{})
)

func main() {
    callback := js.NewCallback(printMessage)
    defer callback.Release()
    setPrintMessage := js.Global().Get("setPrintMessage")
    setPrIntMessage.Invoke(callback)
    beforeUnloadCb := js.NewEventCallback(0, beforeUnload)
    defer beforeUnloadCb.Release()
    addEventLtstener := js.Global().Get("addEventListener")
    addEventListener.Invoke("beforeunload", beforeUnloadCb)
    <-beforeUnloadCh
    fmt.Prtntln("Bye Wasm!")
}

func printMessage(args []js.Value) {
    message := args[0].String()
    no++
    fmt.Prtntf("Message no %d: %s\n", no, message)
}

func beforeUnload(event js.Value) {
    beforeUnloadCh <- struct{}{}
}

Раньше, на стороне JS, загрузка двоичного файла wasm выглядела так:

?const go = new Go()
let mod, inst
WebAssembly
    .instantiateStreaming(fetch("test.wasm"), go.importObject)
    .then((result) => {
        mod = result.module
        inst = result.Instance
        document.getElementById("runButton").disabled = false
    })

Давайте адаптируем её для запуска двоичного файла сразу после загрузки:

?(async function() {
    const go = new Go()
    const { instance } = await WebAssembly.instantiateStreaming(
        fetch("test.wasm"),
        go.importObject
    )
    go.run(instance)
})()

И заменим кнопку «Run» полем сообщения и кнопкой для вызова printMessage():

?<input id="messageInput" type="text" value="Hello Wasm!">
<button onClick="printMessage(document.querySelector('#messagelnput').value);"
        id="prtntMessageButton"
        disabled>
    Print message
</button>

Наконец, функция setPrintMessage(), которая принимает и сохраняет обратный вызов, должна быть проще:

?let printMessage;

function setPrintMessage(callback) {
    printMessage = callback;
    document.querySelector('#printMessageButton').disabled = false;
}

Теперь, когда нажимаем кнопку «Print message», должны увидеть сообщение по нашему выбору и счетчик вызовов, напечатанный в консоли браузера.
Если установим флажок «Preserve log» консоли браузера и обновим страницу, увидим сообщение «Bye Wasm!».



Исходники доступны в папке examples/long-running на github.

А дальше?


Как можете видеть, изученный syscall/js API делает своё дело и позволяет писать сложные вещи небольшим количеством кода. Можете написать автору, если знаете способ проще.
На данный момент невозможно вернуть значение в JS непосредственно из обратного вызова Go.
Надо иметь ввиду, что все обратные вызовы выполняются в одной и той же goroutin'е, поэтому, если вы делаете некоторые блокирующие операции в обратном вызове, не забудьте создать новую goroutin'у, иначе вы заблокируете выполнение всех остальных обратных вызовов.
Все основные функции языка уже доступны, включая параллелизм. Пока все goroutin'ы будут работать в одном потоке, но это изменится в будущем.
В наших примерах использовали только пакет fmt из стандартной библиотеки, но доступно всё, что не не пытается сбежать из песочницы.

Кажется, что файловая система поддерживается через Node.js.

Наконец, как насчет производительности? Было бы интересно запустить некоторые тесты, чтобы увидеть, как Go wasm сравнивается с эквивалентным чистым JS-кодом. Некто hajimehoshi сделал замеры, как разные среды работают с целыми числами, но методика не очень понятна.



Не надо забывать, что Go 1.11 ещё даже не вышел официально. По-моему очень неплохо для экспериментальной технологии. Те, кому интересны тесты производительности, могут помучать свой браузер.
Основная ниша, как отмечает автор — перенос с сервера на клиент уже существующего go кода. Но с новыми стандартами можно делать полностью offline приложения, а wasm код сохраняется в скомпилированном виде. Можно много утилит в web перенести, согласитесь, удобно?

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (60):

  1. Sabubu
    /#18896161

    > можно писать Frontend на Go
    > Звучит многообещающе

    Не соглашусь. Вы можете зайти в интерфейс Google Doubleclick for Publishers, чтобы увидеть, что получится. Там используется Dart-код, который скомпилирован в огромные JS файлы, размером по моему порядка 2-3 Мб. Причем виджет «What's new» идет как отдельный файл такого же размера — похоже, что компилятор заново приинклудил туда все стандартные библиотеки. Грузится это адски медленно и при работе тормозит.

    Так что я не хочу фронтенд на Го. Давайте лучше фронтенд на HTML5 и CSS 2.1 делать.

    • Houston
      /#18896279 / +1

      Frontend на Go

      Там используется Dart-код

      Но… это же абсолютно разные вещи?

    • OlegSchwann
      /#18896301 / +2

      Если альтернатива — устанавливать что-то на компьютер, то почему бы и нет. Wasm кешируется в скомпилированном виде и работает сразу. Для desktop пара мегабайт не считается чем-то значимым.
      Вот можно зайти и поиграть в quake, оригинальный код, скомпилированный в wasm. Работает очень гладко, грузится быстро, действий от пользовалеля, кроме перехода по ссылке, не требует.

      • RPG18
        /#18897083

        Скомпилированный wasm сейчас кешируется только в firefox, остальные браузеры отключили эту возможность из-за безопасности. Firefox это где-то 12% всех браузеров.

      • LeNsTR
        /#18898419

        QuakeJS is a port of ioquake3 to JavaScript with the help of Emscripten.

        Насколько я понимаю, там всё же не wasm, а asm.js

        • OlegSchwann
          /#18901537

          Согласен. Хотя всё равно как бинарник выглядит, если в середину промотать.

          Хм, получается, что wasm ещё быстрее работать будет?

    • KYuri
      /#18896315

      Пару моментов:

      • Трансляция чего-либо в JS — это совсем не то же, что и трансляция этого же чего-либо в WebAssembly
      • Кто сказал, что на Go нужно писать весь фронтэнд? Вполне можно из JS-а вынести «тяжелые» расчёты (криптографию, например)

      • Sabubu
        /#18896487

        Во-первых, низкоуровневый язык (wasm) будет требовать больше байт памяти, чем высокоуровневый. Потому маленьким объем не будет. Плюс, люди любят использовать разные библиотеки, и они будут еще сильнее увеличивать объем. Плюс, у языка может быть рантайм, который сам по себе потребует место.

        Почему тормозит код гугла на дарт — я не знаю. Может из-за того, что там используется Material Design с анимациями, может из-за этого. Но получился по моему мнению, отвратительного качества продукт. Это он в Хроме тормозит, а в Фаерфоксе или ИЕ вообще страшно туда даже заходить.

  2. dpigo
    /#18896287

    А у вас специально в коде местами i (ай) заменена на l (эл)?

    func maln, ?let prlntMessage, #prlntMessageButton

  3. OlegSchwann
    /#18896299

    dell

  4. kozyabka
    /#18896341

    А для чего это нужно на практике? Есть же ангуляр, ву, рекат и вот это всё. Теперь предлагается написать еще один фреймворк только уже на го?

    • maxzh83
      /#18896393

      WebAssembly это не очередной фреймворк, это возможность исполнения более низкоуровневого байткода браузером. А это открывает возможность писать фронтенд на языках отличных от JavaScript, вот тут на Go. Интересно пойдет ли это в массы и что будет с JavaScript, когда у него не останется монополии в поддержке браузерами.

      • Sabubu
        /#18896501

        Вообще, заменить JS на Go может быть не лучшей идеей. У Го довольно тяжелый рантайм, свой сборщик мусора, ему нужны системные вызовы (+ еще объем для их эмуляции), он там создает кучу потоков. Плюс, если вы хотите взаимодейстовать с DOM, вам придется постоянно выходить из wasm среды в JS интерпретатор и это тоже наверно повлечет расходы.

        Компилировать в wasm имеет смысл код для каких-то тяжелых задач — обработка картинок, видео, аудио, генерация PDF и так далее. Для работы с DOM выгоднее использовать JS наверно.

        • maxzh83
          /#18897491

          Вообще, заменить JS на Go может быть не лучшей идеей

          Если вы отвечали мне, то я не говорил что это лучшая идея. Я говорил о равных условиях для всех языков при написания фронтенда. Go это будет или C# или еще что-то — это дело вкуса.
          если вы хотите взаимодейстовать с DOM

          да, это пока самая большая проблема с wasm, пока они это не решат, технология не зайдет в массы.

          • RPG18
            /#18897791 / +1

            Решение есть. Дергать JS, для манипуляции с DOM.

            • maxzh83
              /#18897975

              Это не решение, это костыль.

              • RPG18
                /#18898199

                Этого более чем достаточно, для production.

      • argonavtt
        /#18897365

        Ничего не будет, ни один проект не будет делать фронт на Go, если это не сайт для кота конечно. 1) Это не быстро. 2) Это требует штат программистов на go. 3) Это сырая технология которой угнаться за современным вебом будет трудно. 4) Отсутствие хоть какого то здравого смысла. Зачем писать на go если js это стандарт который вырабатывался десятилетиями? Сори, но кроме как баловством это ни как не назвать.

        • maxzh83
          /#18897687

          Вы тоже не поняли моего посыла, я не про Го. Я о том, что технология потенциально позволяет писать на чем угодно. Но из ваших аргументов, я могу согласится только с 3), это пока очень и очень сыро.
          Во всем остальном совсем не убедительно.

          1) Это не быстро.

          Если посмотреть, что творится в js с кучей webpack, babel, TypeScript и прочих постпроцессоров, то сомневаешься, что это быстро.
          2) Это требует штат программистов на go

          Написание на js требует штат программистов на js
          Зачем писать на go если js это стандарт который вырабатывался десятилетиями

          Зачем писать на js, если будет новый стандарт, который не обязывает этого делать?

          • argonavtt
            /#18898101 / +1

            1) Все эти webpack, babel, TypeScript ставятся одной командой. Да и речь идёт о производительности а не сборе проекта.
            2) Штат front-end программистов найти гораздо легче, это проще для бизнеса. Да и не каждый программист на go согласиться заниматься front-end'ом. В добавок ко всему нужно знать не только язык, но и особенности браузера и много чего ещё, т.е. вы должны найти go разработчика который ещё и front-end'ер сам по себе.
            4) Браузеры не просто так поддерживают js, по этому это и есть стандарт. Если бы браузеры по умолчанию поддерживали go то он был бы стандартом, а так это всего лишь сомнительная возможность при помощи хаков.

            • maxzh83
              /#18898181

              1) Байт код будет однозначно быстрее
              2) Не надо искать именно на Го, надо искать на любом языке, который будет с поддержкой wasm
              3) Речь же не про поддержку браузерами Го, а про поддержку браузерами (более) абстрактного байт кода, без привязки к js. Примерно так сделаны JVM и CLR

              • RPG18
                /#18898215

                Байт код проще парсить. Производительность зависит от конкретной реализации в браузере.

              • argonavtt
                /#18898453 / +1

                1) Это от задачи зависит, на фронте 90% задач это вывод какой то простых данных с сервера, использовать wasm для решений подобных задачь это всё равно что стрелять из пушки по воробьям. Другое дело конечно как выше уже писали «Doom в браузере».
                2) А разница? На других языках точно так же будет не просто найти такого специалиста.
                3) Пишется всё равно на go

        • guai
          /#18899181

          Это сырая технология которой угнаться за современным вебом будет трудно

          Современный вэб сам гоняется за собственным хвостом, за чем там угоняться? Ни html, ни js, ни css кардинально не менялись с самого возникновения.
          За каждым новомодным js-фреймворком разве что :)
          wasm да http2 хоть немного что-то новое привнесут, когда получат большее распространение.

          • argonavtt
            /#18899207

            Ну да, не меняются image

            • guai
              /#18899291

              Я лучше понимаю аргументы, когда они выражены словами, а не мемасными картинками.
              Вот еще вебсокеты вспомнил, но это когда было-то.
              По сравнению с другими стэками технологий, вэб очень ригиден, оно и понятно, куча разрабов браузеров и вэб-серверов должны каждое новое изменение успеть реализовать, а юзеры обновиться — и это по всему миру, занимает годы.

              • argonavtt
                /#18899321

                Нужно быть достаточно далёким от веба человеком, что бы утверждать, что он не развивается. GraphQL ещё вспомните если для вас развитие веба упирается во взаимодействие сервера и клиента.

                • guai
                  /#18899379

                  я не говорил, что он не развивается вообще, я сказал, что он кардинально не менялся никогда до недавнего времени — до времени wasm и http2
                  Очередные рюшечки типа круглых кнопочек я, да, не считаю кардинальными изменениями.
                  Давайте списочек кардинальных изменений, если вы считаете, что они были и их было много, хотя б с пяток наберется?
                  Даже XHTML не взлетел.

                  • argonavtt
                    /#18899423

                    Не до конца понял, что вы имеете ввиду под «кардинальными» изменениями, новый язык разметки, стилей, логики? Таких не будет, они в принципе не возможны, что бы это понять нужно понять как работает веб. Даже простые изменения внедряются аккуратно, чтобы ничего не сломать. И да, данное изменение не «кардинальное» т.к. это по сути хак, который при том работает не стабильно.

                    • guai
                      /#18899581

                      Ну да, вот я и говорю, гнаться новой технологии там не за чем. Берем и делаем то же самое, что и все остальные. И пока эта работа будет вестись, ничего в вэбе не изменяется ни на йоту.

                      • argonavtt
                        /#18899591

                        Там в имеющихся технологиях хватает изменений, только успевай изучать.

                        • guai
                          /#18899725

                          Да что изучать-то?
                          1 новый тэг, которые раз в 5 лет добавляют? или новый js-фрэймворк? ну это скорее прихоть, а не необходимость, старые-то не перестают работать.
                          поддержка http2 скорее затронет разрабов серверов, а не рядовых кодеров, ну может еще разрабов фрэймворков.
                          А кроме этого, я не понимаю, что там изучать можно.

                          • argonavtt
                            /#18899809

                            GraphQL, WebGL. Это только чуть капнув. В самом js постоянные изменения. Да и к слову про фреймворки, если это для вас основное что есть во фронте, они меняются, постоянно выходят новые версии, в пристройку к ним идёт куча всего, и новый фреймворк хоть и не обязательно узучать, ознакомиться всегда не помешает.

                    • evocatus
                      /#18900919

                      Например: в HTML нельзя создать в форме несколько кнопок с разными HTTP-методами. До сих пор невозможно создать кнопки в форме с методом UPDATE или DELETE. До сих пор стандартный тег невозможно стилизовать.

                  • Nookie-Grey
                    /#18900747

                    да хоть тот же самый display:flex; хорошо подвинул float & inline-block

    • OlegSchwann
      /#18896455

      Наверное, пока основная задача — разделять код логики сервера с клиентом. Ещё wasm показывает себя с лучшей стороны, когда делает что-то, под что не оптимизирован js. Например, потоковая обработка видео. А работать с DOM деревом wasm действительно не умеет — всё через API JS.

  5. Dmitry88
    /#18896411

    Что-то туплю,
    Похвально, что можно такое провернуть, но как мне кажется, что здесь ошибочный заголовок со словом фронтэнд. Можно реализовать много чего, но зачем подменять полноценный фронтэнд такой реализацией?

  6. newpavlov
    /#18896421 / +2

    А теперь давайте сравним размеры итогового wasm файла условного нетривиального Hello World написанного на Go с примерно такой же программой написанной на Rust/C++. (про текущую производительность график в статье и так достаточно нагляден) Плюс о каких преимуществах Go может идти речь в сугубо однопоточной среде WebAssembly? Без этого Go превращается в непримечательное подобие C со сборщиком мусора. Понятно что хочется тоже запрыгнуть на хайп-трейн, но лично я к Go скомпилированному в WebAssembly весьма скептично.

    • OlegSchwann
      /#18896513

      Потоки будут. + go проще, те же факторы, что и при разработке backend.
      Хотелось бы знать, как это будет выглядеть через пару лет.

      • RPG18
        /#18897115

        Потоки будут, но неизвестно когда. Emscripten, который LLVM бакенд, сделал потоки через SharedArrayBuffer, но из-за атаки Spectre эту возможность временно отключили. Сейчас потоки есть у Rust в yew, т.к. они сделали модель акторов над Web Workers, без общей памяти.

    • youROCK
      /#18896671

      Да какая разница, какой язык :)? Главное, что это язык с нормальной типизацией и фазой компиляции, и на нем не лежит весь груз обратной совместимости со старым кодом.

      • newpavlov
        /#18896679

        Как уже выше писали, основная проблема в толстом рантайме. Возможно эту проблему и получится частично решить более продвинутой линковкой и кешированием (что бы, например, рантайм Go был отдельным wasm файлом и расшаривался между сайтами), а так же интеграцией с браузерным сборщиком мусора, но это дело точно не ближайшего будущего. По этим причинами, на мой взгляд, для wasm больше всего подходят C, C++ и Rust, ну может быть D с его Better C (хотя новостей о нём в области wasm я особо не слышал). Причём Rust весьма активно инвестирует в данную область.

        • RPG18
          /#18897169

          Все постигается в сравнении. Без бенчмарков говорить про тольстый рантайм рановато. Unity3D штука не легкая, но вполне можно поиграть в Dead Trigger 2 из браузера.

        • guai
          /#18899239

          У D есть компилятор под LLVM, которая уже умеет в wasm. Но пока в wasm не запилят сборщик мусора, с которым можно было бы связать D-шный сборщик, код на таком D будет не очень D-образным, с ручной аллокацией-деаллокацией.
          Если я не ошибаюсь, полноценная работа со строками требует GC, некоторые встроенные коллекции, и т.п.

          • RPG18
            /#18899479

            о пока в wasm не запилят сборщик мусора, с которым можно было бы связать D-шный сборщик, код на таком D будет не очень D-образным, с ручной аллокацией-деаллокацией.

            Не будет. То что нет GC в wasm, не значит что GC не может быть на уровне языка, как часть его runtime.

            • guai
              /#18899639

              Ну так-то да, можно с собой таскать, но это будет раздувать размеры сборок. В принципе, можно спереть от сишки, но я не слышал, чтоб кто-то это делал.
              Да и зачем, если можно подождать? В wasm же обещают эту фичу когда-то запилить.

              • RPG18
                /#18899669

                Насколько будет раздувать? Например наше приложение имеет размер 19Mb, с сжатием передается 6Mb. Задержки на компиляцию заметны только на мобильных устройствах.

                • guai
                  /#18899775

                  Ну этого я не знаю, возможно, и не существенно. Но будет же по-любому.
                  Хотя, в сравнении с js, может, и меньше будет в итоге.
                  Сам GC наверн не много займет, десятки килобайт, думаю. Но еще какой-то кусок стандартной библиотеки надо притащить, чтоб комфортно жилось.
                  Вопрос — будут ли там модули какие-нибудь и кэширование их, чтоб не приходилось каждый раз качать заново. Наверн, со временем будет.

                  • RPG18
                    /#18899865

                    Что бы не качать заново, есть стандартное кеширование.

                    • guai
                      /#18900529

                      Имеете в виду кэш ресурсов? Его мало. Надо еще, чтоб код из разных файлов слинковался, версии проверять надо, сертификаты — ну с модулями немало заморочек. Хрен их знает, сделали это уже, или пока только планируют…

      • zim32
        /#18898117

        В том то и дело что уже давно есть Typescript с поддрежкой типизации, дженериков, нормальных классов с инкапсуляцией. Переводить весь фронт на го или раст нет никакого смысла, особенно экономического. Вынести что-то тяжеловесно еможет быть и да. Или портировать готвовую кодовую базу. Но не более.

  7. kxl
    /#18896635

    вот и Go подтянулся… до этого пробоаал blazor (c#) — прикольно, уже можно что-то начать делать…

    • OlegSchwann
      /#18896681

      На русском пара статей всего. Как вы считаете, настолько технология production ready?

      • kxl
        /#18901131

        С одной стороны бета (даже скорее альфа) версия, с другой — уровень production ready зачастую можем определить и самостоятельно. Это просто риск :)
        Для небольших поделок, когда к какому-то сервису прикрутить простенький GUI пойдет. На большее — оно пока в альфа было… Может что и поменяется в API.
        Да, видел байдинги к DevExtreme. Не успел разобраться ещё, но думаю попробовать что-то сбацать…

  8. amgorb
    /#18897861

    А воообще запускать скомпилированный бинарный код в браузере… это же не безопасно, мы разве к этому идем? у Java не получилось

    • OlegSchwann
      /#18898205

      В том-то и отличие, что безопасно. Бинарный код заперт в песочнице конкретного размера, и уничтожается, при попытке обратится за её пределы. Также, там нет системных вызовов, всё делается только через JS API, которое считается безопасным.

      JVM и WASM серьёзно отличаются друг от друга.
      WASM вписывается в существующую архитектуру web, не пытается тащить свои виджеты.
      WASM добавлен в движок JS, стандарт является открытым.
      WASM не привязан к какому-либо языку.

      Есть более подробное обсуждение.

  9. zim32
    /#18898047

    А используя emscripten уже можно тензорфлоу запускать на джаваскрипт в браузере. И что? Что за мания пихать все что можно во все что не нужно.

  10. Barabas79
    /#18899523

    Возможно wasm станет в какой-то степени альтернативой умершему флешу.
    И наверное отлично подойдет для создания каких нибудь сложных калькулятор с 3D моделями. Что-то вроде констурктора кухонь, где можно двигать элементы в 3D пространстве, менять их размеры и т.д.
    Т.е. каких-то очень проприетарных приложений.
    На JS и сегодняшних инструментах такие вещи получаются очень сложными и тяжелыми для браузере.
    А обычные веб-приложения, где есть активное и типичное взаимодействие с DOM скорее всего будут не для него, слишком неудобен он для них.