cover image
< Inicio
Web

Integración de la biblioteca Go en una página web JavaScript con WebAssembly

Con WebAssembly puedes integrar diferentes lenguajes de programación. Esto abre nuevas posibilidades para utilizar grandes bibliotecas escritas en un lenguaje en otra plataforma diferente. En este artículo quiero mostrar cómo integrar una librería Go para la tokenización de frases en una página web JavaScript. Normalmente, no se puede ejecutar código Go en un navegador web, pero con la tecnología WebAssembly sí se puede.

WebAssembly

WebAssembly es una especificación del W3C que define un formato binario y de texto para programas ejecutables que se pueden ejecutar en el navegador pero también de forma autónoma. Los programas WebAssembly se ejecutan en una máquina virtual (MV) basada en pilas. Tomando su nombre del lenguaje de programación ensamblador, muy cercano al hardware real, WebAssembly define un lenguaje más independiente del dispositivo que el ensamblador, pero sigue siendo de muy bajo nivel.

En el momento de escribir este artículo, WebAssembly es compatible con el 97,11 % de todos los navegadores del mundo (caniuse.com). Existen varias extensiones a la especificación WebAssembly del W3C que tienen diferentes niveles de soporte entre los navegadores.

Sentences Go Library

Sentences es una librería Go para la tokenización de sentencias escrita por Eric Bower y publicada como código abierto en GitHub. Dado que no se puede ejecutar código Go en el navegador de forma nativa, sólo JavaScript, existe la idea de utilizar WebAssembly para integrar la librería en un sitio web.

Exponer la biblioteca de sentencias a JavaScript

Puedes integrar la librería Sentences escribiendo una aplicación Go wrapper que aproveche el paquete syscall/js de Go. Es parte de la librería estándar de Go. Ten en cuenta, que en el momento de escribir este artículo, este paquete está etiquetado como "experimental". En concreto, utilizaremos la función js.FuncOf. ¡Aquí está el código fuente completo de la aplicación Go:

package main

import (
	"fmt"
	"strings"
	"syscall/js"

	"github.com/neurosnap/sentences/english"
)

func sentenceWrapper() js.Func {
	sentenceFunc := js.FuncOf(func(this js.Value, args []js.Value) any {
		if len(args) != 1 {
			return "Invalid no of arguments passed"
		}
		inputSentence := args[0].String()
		fmt.Printf("input %s\n", inputSentence)
		tokenizer, err := english.NewSentenceTokenizer(nil)
		if err != nil {
			panic(err)
		}

		sentences := tokenizer.Tokenize(inputSentence)
		for _, s := range sentences {
			fmt.Println(s.Text)
		}

		var sentenceTexts []string
		for _, s := range sentences {
			trimmedText := strings.TrimSpace(s.Text)
			sentenceTexts = append(sentenceTexts, trimmedText)
		}

		allSentences := strings.Join(sentenceTexts, "\n") // Join all sentences separated by a space
		return allSentences

	})
	return sentenceFunc
}

func main() {
	fmt.Println("Go Web Assembly")
	js.Global().Set("tokenizeSentence", sentenceWrapper())
	<-make(chan struct{})
}

Ves que ahora la función sentenceWrapper se expone como la función tokenizeSentence en JavaScript. Espera como argumento una cadena a tokenizar y devuelve la cadena tokenizada. La integración real con la librería sentences ocurre dentro de la función pasada a js.FuncOf.

Compilar Wrapper

Compila la aplicación a WebAssembly usando el siguiente comando Go build:

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

Si todo ha ido bien, deberías encontrar el archivo main.wasm como salida en tu carpeta. Este es el binario WebAssembly que se cargará en la aplicación JavaScript.

Integrar la librería Go WebAssembly en la página web

Para cargar el archivo main.wasm en la página web, necesitas el siguiente código:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <script src="wasm_exec.js"></script>
    <script>
      const go = new Go();
      WebAssembly.instantiateStreaming(
        fetch("./main.wasm"),
        go.importObject
      ).then((result) => {
        go.run(result.instance);
      });
    </script>
  </head>
  <body>
    <textarea id="sentenceinput" name="jsoninput" cols="80" rows="20"></textarea>
    <input
      id="button"
      type="submit"
      name="button"
      value="tokenize"
      onclick="tokenize(sentenceinput.value)"
    />
    <textarea id="jsonoutput" name="jsonoutput" cols="80" rows="20"></textarea>
  </body>
  <script>
    var tokenize = function (input) {
      jsonoutput.value = tokenizeSentence(input);
    };
  </script>
</html>

Cuando ejecute esta página web en el navegador, también necesitará el archivo de utilidad JavaScript wasm_exec.js en el directorio actual. Puede obtener el archivo de:

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

Como puedes ver en el código JavaScript, la función expuesta tokenizeSentence() es llamada desde JavaScript cuando se pulsa el botón "tokenize". El contenido del campo de entrada se utiliza como argumento de la función y el valor de retorno se escribe en el campo de texto de la derecha. Tenga en cuenta que necesita ejecutar un servidor web para poder ejecutar la página web en su navegador. Puedes usar este código para construir un servidor web en Go:

server/server.go

package main

import (
	"fmt"
	"net/http"
)

func main() {
	err := http.ListenAndServe(":9090", http.FileServer(http.Dir("../")))
	if err != nil {
		fmt.Println("Failed to start server", err)
		return
	}
}

Después de iniciar el servidor web, puedes acceder a la página en tu navegador bajo: http://localhost:9090.

Gracias a Naveen Ramanathan de golangbot.com por su inspiración para este artículo.

Conclusión

Como siempre, espero que este artículo te haya ayudado de alguna manera a mantener la curiosidad. WebAssembly es una tecnología que tiene un gran potencial para integrar los diferentes ecosistemas de lenguajes entre sí.

Referencias

Photo by Venti Views on Unsplash

Publicado el

6 jul. 2024


Creative Commons License

Este trabajo está licenciado bajo una Creative Commons Attribution 4.0 International License.
Thomas Derflinger

Escrito por Thomas Derflinger

Soy un empresario visionario y desarrollador de software. En este blog escribo principalmente sobre programación web y temas relacionados como el IoT.