Introdução: Por que Go e WebAssembly em nível avançado?
\n\nO WebAssembly (Wasm) evoluiu de uma tecnologia experimental para um padrão para aplicações web de alto desempenho. Go, com sua compilação eficiente e suporte nativo a Wasm, é uma das melhores linguagens para criar módulos que operam no limite das capacidades do navegador. Exemplos básicos de "Hello, World!" já não são relevantes — as tarefas modernas exigem controle refinado de memória, troca bidirecional de dados com JavaScript e otimização do tamanho do arquivo binário.
\n\nNeste artigo, abordaremos técnicas avançadas de trabalho com WebAssembly em Go. Você aprenderá como transmitir estruturas de dados complexas, chamar funções Go a partir do JavaScript e vice-versa, além de como otimizar o desempenho dos seus módulos Wasm. O material é voltado para desenvolvedores que já conhecem os fundamentos de Go e desejam elevar suas habilidades a um novo nível.
\n\n1. Gerenciamento de memória: transmissão de strings, arrays e estruturas
\n\nUm dos principais problemas ao trabalhar com WebAssembly é a transmissão de dados complexos entre a memória linear do Wasm e o heap do JavaScript. Go usa seu próprio coletor de lixo, o que impõe limitações. Vamos explorar métodos avançados.
\n\n1.1. Copiando strings com `copyBytesToGo` e `copyBytesToJS`
\n\nA transmissão direta de strings por parâmetros de função não é eficiente para grandes volumes de dados. Em vez disso, use os métodos do pacote 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 // Obtém o comprimento da string\n length := jsStr.Get("length").Int()\n // Cria um buffer na memória Go\n buf := make([]byte, length)\n // Copia os dados do JS para Go\n js.CopyBytesToGo(buf, jsStr)\n // Agora buf contém os bytes UTF-8 da string\n // ... processamento ...\n // Retorna o resultado de volta\n result := []byte("Resposta: " + 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. Transmissão de estruturas via serialização JSON
\n\nPara estruturas aninhadas complexas, use JSON como formato intermediário. Isso é mais lento que o trabalho direto com memória, mas significativamente mais conveniente:
\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": "sem dados"})\n }\n // Obtém a string JSON do JS\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 // Processamento dos dados\n user.Age++\n // Retorna o JSON de volta\n result, _ := json.Marshal(user)\n return js.ValueOf(string(result))\n}\n\n\n2. Comunicação bidirecional: chamando JavaScript de Go e vice-versa
\n\nA integração avançada exige que o módulo Go não apenas responda a chamadas, mas também inicie ações no ambiente JavaScript.
\n\n2.1. Registro de callbacks Go para JavaScript
\n\nUse js.FuncOf para criar funções visíveis a partir do JS. É importante gerenciar corretamente seu ciclo de vida:
var callback js.Func\n\nfunc registerCallback() {\n callback = js.FuncOf(func(this js.Value, args []js.Value) interface{} {\n // Obtém o callback do JS\n jsCallback := args[0]\n // Chama-o com dados do Go\n go func() {\n // Simula trabalho assíncrono\n time.Sleep(100 * time.Millisecond)\n jsCallback.Invoke(js.ValueOf("Resultado do Go"))\n }()\