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

Resetear GOOJPRT PT-210 MTP-II (Impresora térmica)

El día de hoy vamos a ver cómo restablecer la impresora térmica GOOJPRT PT-210 a…

3 días hace

Proxy Android para impresora térmica ESC POS

Hoy voy a enseñarte cómo imprimir en una impresora térmica conectada por USB a una…

3 días hace

Android – Servidor web con servicio en segundo plano

En este post voy a enseñarte a programar un servidor web en Android asegurándonos de…

3 días hace

Cancelar trabajo de impresión con C++

En este post te quiero compartir un código de C++ para listar y cancelar trabajos…

1 mes hace

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é…

2 meses 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…

2 meses hace