This page covers the internals of how the simplest lofigui app works: the request lifecycle, polling mechanism, graceful shutdown, and the WASM service worker bootstrap.
When the WASM demo runs in the browser, a service worker intercepts HTTP requests and routes them to Go's net/http handlers running inside the WASM binary. The bootstrap sequence has several async phases:
go-wasm-http-server library registers a fetch event listener synchronously, but the WASM binary loads asynchronously. All intercepted requests call event.respondWith(handlerPromise.then(handler)) — they are queued until the Go code calls wasmhttp.Serve(mux), which resolves the promise. There is no timeout: if the WASM fails to compile, requests hang indefinitely.
skipWaiting() activates immediately; clients.claim() takes control of the bootstrap page so that fetch() calls from JavaScript go through the SW rather than directly to the static server.
getRegistrations() and unregisters all existing service workers before registering the new one. Without this, a stale SW from a previous directory layout can intercept requests and serve incorrect responses.
app.Handle(model) combines start and display into one endpoint. It uses the buffer as state:
| Buffer | Action running | Behaviour |
|---|---|---|
| Empty | No | Start model goroutine, render with Refresh header |
| Any | Yes | Render current buffer with Refresh header (polling) |
| Non-empty | No | Render final output, no Refresh header (done) |
When the model goroutine returns normally, Handle calls flush() automatically:
http.Server.Shutdown()The server returns nil from app.ListenAndServe on graceful shutdown (exit code 0). Panics and bind errors return non-nil (exit code 1).
HandleRoot/HandleDisplay (later examples) call EndAction() directly. The server stays alive for restart. Flush() is available as a public method for models that want to trigger shutdown explicitly.
For the simplest case, NewApp() provides sensible defaults that later examples override:
| Default | Value | Override |
|---|---|---|
| Controller | Built-in Bulma template (created lazily on first request) | app.SetController(ctrl) |
| Refresh time | 1 second | app.SetRefreshTime(n) |
| Display URL | /display |
app.SetDisplayURL(url) |
| Favicon | Auto-registered on DefaultServeMux | Register your own /favicon.ico handler first |
For apps that need restart support or a long-lived server, use separate endpoints:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
app.HandleRoot(w, r, model, true)
})
http.HandleFunc("/display", func(w http.ResponseWriter, r *http.Request) {
app.HandleDisplay(w, r)
})HandleRoot resets the buffer, starts the model, and redirects to /display. HandleDisplay renders the current state. The model calls EndAction() when done. The server stays alive — visiting / again restarts the model.