Durante los últimos años, WebAssembly fuera del navegador ha crecido despacio pero con dirección. El gran cuello de botella ha sido siempre el mismo: el modelo de concurrencia. Preview 1 no tenía nada que se pareciera a hilos o async; preview 2 abrió la puerta al modelo de componentes pero dejó la concurrencia como asunto pendiente. Preview 3, que lleva meses en la fase final de estandarización, es por fin la pieza que faltaba, y merece la pena entender por qué es importante.
Este post no pretende ser un tutorial exhaustivo de la API, sino un mapa mental: qué problema resuelve preview 3, cómo lo aborda, y qué implicaciones tiene para los lenguajes y plataformas que lo adopten.
El problema que arrastrábamos
Antes de preview 3, ejecutar código WebAssembly concurrente fuera del navegador exigía trucos externos. Los runtimes (Wasmtime, WasmEdge) inventaban sus propias extensiones para offrecer multi-threading, y cada lenguaje compilado a Wasm tenía que acomodarse como podía. Rust compilaba a wasm32-unknown-unknown sin soporte de hilos, y si querías hilos, necesitabas runtimes específicos y técnicas de shared memory manualmente orquestadas.
El resultado era una plataforma que vendía portabilidad pero no la entregaba cuando tu aplicación necesitaba hacer dos cosas a la vez. Para cualquier carga seria (un servidor HTTP, un procesador de streams, un pipeline con concurrencia natural) tenías que elegir entre limitarte a lo sincrónico o saltar fuera del estándar.
El modelo que propone preview 3
Preview 3 introduce lo que el estándar llama concurrencia estructurada, y es más interesante que «hilos y async». En vez de ofrecer las primitivas de bajo nivel de un sistema operativo (mutex, condiciones, locks), preview 3 eleva el modelo a primitivas componibles: futures y streams.
Un future en preview 3 es exactamente lo que piensas: una operación que todavía no ha terminado, pero que promete producir un valor. Un stream es una secuencia de valores que llegan en el tiempo, no todos juntos. Con esas dos primitivas, más la noción de tareas (tasks), se puede construir prácticamente cualquier patrón de concurrencia conocido, desde una llamada HTTP asíncrona hasta un pipeline de procesamiento en streaming.
Lo relevante es que estas primitivas son del modelo de componentes, no del runtime. Eso significa que un lenguaje que compile a Wasm puede ofrecer su propia sintaxis de async/await y traducirla a futures del modelo de componentes en el binario final. Y cuando ese binario interactúa con otro componente (potencialmente escrito en otro lenguaje), la concurrencia atraviesa la frontera limpiamente.
Por qué es importante
El valor real de preview 3 no es que Wasm pueda hacer async, sino que lo puede hacer de forma estándar a través de los límites entre lenguajes. Esto tiene varias consecuencias prácticas.
Primero, un servidor HTTP escrito en Rust puede llamar, dentro del mismo binario, a un módulo Wasm escrito en Go que procesa streams. La llamada al módulo puede ser asíncrona, y el flujo de datos entre ambos puede ser streaming. Antes, o dos estabas atrapado en la misma implementación (todo Rust) o hacías la frontera sincrónica (lo que mata el rendimiento para cargas intensivas).
Segundo, plataformas que ejecutan WebAssembly en producción (Fastly Compute, Cloudflare Workers en algún modo experimental, Fermyon Spin, varias funciones serverless) pueden ahora soportar patrones asincrónicos de forma nativa, sin truquillos específicos de cada plataforma. Esto es un paso grande hacia la portabilidad real de aplicaciones serverless Wasm entre proveedores.
Tercero, y quizá lo más importante a largo plazo, los SDKs específicos de cada lenguaje van a converger. Hoy, escribir una función Lambda en Rust implica un SDK que expone primitivas async propias; en Go, otras distintas; en Python, otras. Con preview 3 el estándar empuja a que todas esas SDK hablen el mismo modelo, lo que reduce fragmentación y facilita portar código entre plataformas.
Lo que todavía no está resuelto
Pese al avance, preview 3 deja algunas cosas pendientes.
El mapeo de estas primitivas al modelo de threading nativo de cada runtime sigue siendo responsabilidad del runtime. Eso significa que el comportamiento exacto (¿cuántos hilos reales se crean? ¿cómo se programan las tareas?) puede variar entre Wasmtime, Wasmer y otros. En la práctica, ejecutar el mismo componente con concurrencia intensa en dos runtimes distintos puede dar rendimientos muy dispares, incluso si la corrección está garantizada.
La interoperabilidad con código host nativo (funciones expuestas desde el runtime hacia el componente Wasm) también necesita más trabajo. El estándar define cómo deben comportarse los hosts, pero hay casos límite, como cancelación durante una operación asíncrona, que aún están siendo pulidos.
Y por supuesto, la adopción en lenguajes. Rust va bien encaminado con el trabajo de wasm-bindgen y las ramas experimentales para preview 3. Go, Python y C++ están detrás, con estados variables. Dar tiempo a que el ecosistema converja va a llevar probablemente todo 2025.
Qué significa esto para el desarrollador
Si trabajas en una aplicación que ya usa WebAssembly fuera del navegador, preview 3 es una buena noticia: va a simplificar código que hoy depende de hacks específicos del runtime. Si estás empezando a explorar Wasm como alternativa a contenedores para ciertos casos (funciones serverless, plugins embebidos, aislamiento fuerte con coste bajo), preview 3 es la versión en la que empezar a tomarlo en serio.
Si no has tocado Wasm en absoluto, este sigue siendo un buen momento para mirarlo. La plataforma ha madurado lo suficiente para que las pequeñas asperezas del inicio ya no sean el obstáculo. La aparición de preview 3 cierra el capítulo de «sí, pero…» que arrastraba desde hace años: sí, Wasm funciona, sí, puedes usarlo en serio, sí, puedes hacer concurrencia decente.
Mi intuición es que los próximos doce meses van a ser los que definan si WebAssembly se convierte en una plataforma de despliegue mainstream para servicios serverless o si se queda en un nicho especializado. Preview 3 es probablemente la pieza que decide en qué dirección cae la balanza, y por eso conviene entenderla pronto aunque no sea mañana el día que la uses en producción.