109 / Advanced

await in the browser

Nyx compiled to wasm32-wasi can use real await: an async fn that calls await browser_fetch_await(...) WAITS for the server's response and continues on the next line with the value, without splitting the flow into a closure per request. The new std/browser_await module lives apart from std/browser on purpose: only a program that imports it pays for Asyncify (and binaryen as a build dependency).

109-await-fetch-wasm.nxSource →
// 109 — await en el navegador: una pantalla que carga una factura sin callbacks
//
// Target wasm32-wasi (corre en el navegador, o bajo node con
// examples/browser/run-node.mjs). `await browser_fetch_await(...)` ESPERA la
// respuesta del servidor y la función sigue en la línea siguiente con el
// valor: sin partir el flujo en un cierre por cada pedido.
//
// Construir:  nyx build --target wasm32-wasi
//   (necesita binaryen: `sudo apt install binaryen`, o NYX_WASM_OPT=/ruta/a/wasm-opt;
//    nyx build lo usa SOLO porque el programa espera al anfitrión)
//
// Lo que conviene saber:
//   - El status HTTP no es error: un 404 llega como Ok con r.status == 404.
//     Err es «no hubo respuesta» (kind "connection") o «venció el plazo» ("timeout").
//   - UNA sola función puede estar esperando a la vez. Un click que llega
//     durante el fetch se procesa cuando el fetch termina, en orden.
//   - spawn y los canales siguen sin existir en wasm: await es secuencial.

import "std/browser_await"
import "std/dom"
import "std/error"

async fn cargar_factura(id: int) -> String {
    let res = await browser_fetch_await_opts("/api/facturas/" + int_to_string(id), "GET", "", 5000)
    match res {
        Result.Ok(r) => {
            if r.status != 200 {
                return "la factura " + int_to_string(id) + " no está (HTTP " + int_to_string(r.status) + ")"
            }
            return r.body
        }
        Result.Err(e) => { return "sin respuesta del servidor: " + e.kind + " (" + e.msg + ")" }
    }
}

// El botón «abrir» de cada fila llama a este export con el número de factura.
#[export_name = "abrir_factura"]
fn abrir_factura(id: int) {
    dom_set_text("#estado", "cargando la factura " + int_to_string(id) + "...")
    let texto: String = await cargar_factura(id)
    dom_set_text("#factura", texto)
    dom_set_text("#estado", "listo")
}

fn main() {
    abrir_factura(7)
}
Illustrative outputstdout
#estado → "cargando la factura 7..."
#factura → (the response body, or the failure's reason)
#estado → "listo"

How it works

The HTTP status is not an error: a 404 arrives as Result.Ok with r.status == 404, and cargar_factura tells them apart by hand. The only thing that lands in Result.Err is getting no response at all — kind "connection" or "timeout", the same Error vocabulary as the rest of the stdlib.

abrir_factura is the export the «open» button of each row calls: it updates the DOM before waiting, waits with await, and updates the DOM again with the result — no callback in between. Only ONE function can be waiting at a time: a click that arrives mid-fetch is processed once the fetch finishes, in order.

Building needs binaryen (wasm-opt) because the generated .ll carries an Asyncify imports marker; a program without #[suspends] compiles byte for byte the same as before. spawn and channels still do not exist in wasm: here await is sequential.