Web Assembly

Copiar bytes de Golang a JavaScript con WebAssembly

Gracias a WebAssembly podemos ejecutar código de otros lenguajes de programación desde el navegador web con JavaScript. La ventaja de WASM es que, aparte de ser rápido, permite programar en otro lenguaje y aprovechar las librerías presentes en el mismo.

Personalmente he usado Golang con WebAssembly para crear un generador de credenciales, códigos QR y códigos de barras. Todo el procesamiento y generación de imágenes se hace con Go, para luego exportar los resultados a JavaScript.

En este post voy a documentar cómo exportar un arreglo de tipo byte ([]byte) de Go a un Uint8Array de JavaScript, ya que, como te lo dije anteriormente, esto sirve cuando creamos un archivo binario con Go y queremos exportarlo a JavaScript.

Además, un Uint8Array sí puede ser transportado a través de un WebWorker usando el structured clone algorithm.

Exponer función

Yo expongo la función así:

js.Global().Set("generarCodigos", js.FuncOf(GenerarCodigos))

Aquí Global() va a devolver el ámbito global desde donde se invoca a la función de WASM.

Si estamos en un WebWorker será self, si estamos en el DOM será window y puede devolver otra cosa según el entorno.

Invocar función que devuelve Uint8Array []byte

Fíjate en que la estoy exponiendo como generarCodigos así que podré invocarla desde JavaScript con self.generarCodigos(), y que realmente la función de Go se llama GenerarCodigos cuyo código dejo a continuación:

func GenerarCodigos(this js.Value, parametros []js.Value) interface{} {
 bytesQueConformanElPDF, err := crearPdfConCodigosQr()
 if err != nil {
  return false
 }
 destinoBytesPDFEnJS := js.Global().Get("Uint8Array").New(len(bytesQueConformanElPDF))
 arreglo := parametros[0]
 longitudArreglo := arreglo.Get("length").Int()
 log.Printf("El arreglo mide %#v", longitudArreglo)
 for indice := 0; indice < longitudArreglo; indice++ {
  objeto := arreglo.Index(indice)
  nombre := objeto.Get("nombre").String()
  existencia := objeto.Get("existencia").Float()
  log.Printf("Nombre %#v existencia %#v", nombre, existencia)
 }
 js.CopyBytesToJS(destinoBytesPDFEnJS, bytesQueConformanElPDF)
 return destinoBytesPDFEnJS
}

Realmente el cuerpo de la función no es relevante. Solo estoy probando si estoy recibiendo correctamente el arreglo de objetos desde JavaScript. La parte importante viene a continuación:

Tengo el siguiente arreglo de bytes que representan un PDF:

bytesQueConformanElPDF, err := crearPdfConCodigosQr()
if err != nil {
 return false
}

Lo importante es que bytesQueConformanElPDF es un []byte.

Copiar arreglo de []byte con js.CopyBytesToJS

Ahora vamos a copiar el arreglo de bytes a JavaScript para devolverlo como resultado de la invocación. Hacemos un nuevo Uint8Array cuya longitud será la misma que la longitud del arreglo que tenemos en Go y que podemos obtener con len:

destinoBytesPDFEnJS := js.Global().Get("Uint8Array").New(len(bytesQueConformanElPDF))

Ese fragmento de código es el equivalente a new Uint8Array() de JavaScript. Luego invocamos a js.CopyBytesToJS indicando el destino y el origen. El destino será el Uint8Array que definimos desde Go usando New, y el origen será el []byte que tenemos en Go, así:

js.CopyBytesToJS(destinoBytesPDFEnJS, bytesQueConformanElPDF)

Y ahora vamos a tener ese Uint8Array como resultado de la invocación de la función. En mi caso lo tengo con Comlink y un worker expuestos a través de una Store de Vue, así que lo uso así:

const generar = async () => {
    const resultado = await dbStore.testing([
        { nombre: "Prueba", existencia: 3 },
        { nombre: "Otra cosa", existencia: 3 },
        { nombre: "Tercer elemento", existencia: 3 },
    ]);
    const blob = new Blob([resultado], { type: "application/pdf" });
    const enlace = document.createElement('a');
    const url = URL.createObjectURL(blob);
    enlace.href = url;
    enlace.download = "desde_js.pdf";
    enlace.click();
    URL.revokeObjectURL(url);
    console.log("El resultado es %o", resultado);
}

Aquí, resultado es el Uint8Array que devolvió la invocación a WebAssembly. En el ejemplo lo estoy descargando pero podemos hacer más cosas.

Conclusión

Así de simple podemos invocar a una función de Go desde JavaScript pasando y recibiendo parámetros complejos, ya que invocamos a la función con un arreglo de objetos que podemos leer en Go, y luego Go nos devuelve un Uint8Array.

Nota importante: aunque aquí uso “Go” y “JavaScript” como si se ejecutaran en entornos separados la verdad es que ambos se van a ejecutar en el navegador web gracias a WebAssembly.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Imprimir PDF con Ghostscript en Windows de manera programada

Revisando y buscando maneras de imprimir un PDF desde la línea de comandos me encontré…

4 semanas hace

Hacer pruebas en impresora térmica Bluetooth Android

Esta semana estuve recreando la API del plugin para impresoras térmicas en Android (HTTP a…

1 mes hace

Limpiar clave PEM

Hoy te enseñaré a extraer la cadena base64 de una clave PEM usando una función…

1 mes hace

Foco con Telegram, apagador de 3 vías, relevador y ESP8266

Encender un foco con un Bot de Telegram es posible usando una tarjeta como la…

1 mes hace

Servidor HTTP en Android con Flutter

El día de hoy te mostraré cómo crear un servidor HTTP (servidor web) en Android…

2 meses hace

Imprimir automáticamente todos los PDF de una carpeta

En este post te voy a enseñar a designar una carpeta para imprimir todos los…

2 meses hace