rontolisp:fetch
(rontolisp:fetch url &optional options)
Starts an outgoing HTTP request, modeled on the JavaScript fetch API, and
immediately returns a promise while the request runs asynchronously. A
promise is an opaque value (it prints as #<PROMISE> and satisfies
rontolisp:promisep); pass it to
rontolisp:await to block until the response arrives and
obtain the result property list
(:status <integer> :body <string> :headers <alist>), or chain a callback
with rontolisp:then.
Because the request is already in flight when fetch returns, several requests
can overlap:
(let ((p1 (rontolisp:fetch "http://example.com/a"))
(p2 (rontolisp:fetch "http://example.com/b"))) ; both requests running
(list (rontolisp:await p1) (rontolisp:await p2)))
Options
The optional second argument is an options property list. Recognized keys:
:method— the HTTP method as a string (default"GET"). Supported methods areGET,HEAD,POST,PUT,DELETE,OPTIONSandPATCH, matched case-insensitively; any other method is an error.:headers— request headers, an alist of(name . value)string pairs.:body— the request body as a string (omit for no body).
The options are validated when fetch is called (like the JavaScript fetch,
which throws synchronously on invalid arguments).
;; GET with request headers (an alist of (name . value) string pairs)
(rontolisp:fetch "http://example.com/api"
(list :headers (list (cons "Accept" "application/json"))))
;; POST with a request body
(rontolisp:fetch "http://example.com/api"
(list :method "POST"
:headers (list (cons "Content-Type" "application/json"))
:body "{\"name\":\"rontolisp\"}"))
Result
fetch itself returns the promise. Awaiting it yields the property list
(:status <integer> :body <string> :headers <alist>), where :headers is an
alist of (name . value) response-header pairs:
(let ((res (rontolisp:await (rontolisp:fetch "http://example.com/"))))
(print (getf res :status)) ; => 200
(print (getf res :body)) ; => "<html>...</html>"
(print (getf res :headers))) ; => (("content-type" . "text/html") ...)
A JSON response body parses into Lisp values with
rontolisp:json-parse, and
rontolisp:json-stringify builds a JSON
request :body from an s-expression.
Backend support
- Interpreter and JVM: use the JDK
java.net.http.HttpClient; the request runs on a background thread from the momentfetchreturns. - WASM: component-only, and a hybrid — the base I/O is WASI 0.3 but
fetch imports
wasi:http@0.2+wasi:io@0.2(asyncwasi:http@0.3does not exist upstream yet; see.todo/02-upgrade-fetch-to-wasi-http-0.3.md). The promise is the in-flightwasi:httpresponse handle, so multiple requests genuinely overlap. Compile with--componentand run with-S http=yplus the async flags. fetch remains a compile error in Preview 1 (core-module) mode, which has no hostwasi:http; the generic promise operations (await,then,promisep) compile in every mode. fetch also works inside arontolisp:http-handlerserve component (a proxy-style handler): run it withwasmtime serve -W gc=y -S http=y— the async flags are not needed there. - Browser playground: truly asynchronous. The interpreter runs in a Web
Worker;
fetchhands the request to the page's main thread, which runs the real browserfetch()(subject to CORS) while the program continues, so requests overlap, andawaitblocks the worker until the response arrives. When cross-origin isolation is unavailable (SharedArrayBufferdisabled) the playground falls back to a synchronous request per fetch — programs behave the same, requests simply do not overlap.
Limitations
- The method must be one of
GET,HEAD,POST,PUT,DELETE,OPTIONS,PATCH. An unsupported:methodis an error: the interpreter and JVM reject it atfetchtime; the WASM backend resolves the method statically and rejects a statically-known unsupported:methodat compile time (a method computed at runtime cannot be checked there and is treated as GET, while a runtime-computed:bodyis sent normally). - A failed request (for example a refused connection) surfaces when the promise
is awaited — the same timing as a JavaScript
awaitrejection: it raises an error in the interpreter and JVM, andawaitreturnsnilin WASM. In WASM a request that cannot even be started (for example a malformed URL) makesfetchreturnnilinstead of a promise, and awaitingnilyieldsnil. - In WASM, the response body is capped (about 576 KiB) and very large programs may exhaust the shared linear memory the response buffers reuse.