Reference · Requests and pages
events
events(fn, map | null=) -> responseWhat it does
A response of Server-Sent Events, sent as the function makes them. The function gets
send; each value given to it goes out as JSON in an event's data, with an event
name when send is given a second argument, and send(null) is a comment, which
keeps a quiet connection open. The second argument may be a map, send(value, { event, id, retry }): the event's name, an id, which a browser sends back as Last-Event-ID
when it connects again (listen does), and retry, how many milliseconds it waits
before it does. send(null, { id }) sends the id alone. The content-type is text/event-stream with
cache-control: no-cache. The options are status (200 unless given, 200 to 599,
but not 204, 205 or 304, which have no body) and headers, taken as
respond takes them. A page follows the events with listen
or receive. See
Streaming.
Examples
route GET /jobs/:id/progress = events((send) => { for step in range(10) { work(id, step) send({ done: step + 1, of: 10 }, "progress") // a value, and an event name }})// Goes on from where a dropped connection left off.route GET /feed = events((send) => { let since = int(request.headers["last-event-id"] ?? "0") ?? 0 for item in items_after(since) { send(item, { event: "item", id: str(item.id), retry: 2000 }) }})component Progress(job: int) { state done = 0 listen(`/jobs/${job}/progress`, (p) => { done = p.done }) <progress value={done} max="10" />}The value can be made anywhere; its status and headers say what the head will be,
and its body is null, since the function makes the events while they are sent:
let e = events((send) => send({ n: 1 }, "tick"))print(e)print(e.status, e.headers, e.body)<response 200>200 { "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no" } nullEdge cases
- There is no
typeoption: an event stream is alwaystext/event-stream, and atypegiven as text is ignored; one that is not text is an error, as forstream.headerscan still replace any header,content-typeincluded, and a header'snullleaves it out ({ "cache-control": null }sends none). - A status with no body (204, 205, 304), given as the option or by
reply, is an error before anything is sent, as forstream. x-accel-buffering: nois set, so proxies pass events on as they come.- The function runs when the answer is sent, not when
eventsis called. - When the client goes away, the next
sendends the function, so a function that loops withsleepends when the viewer leaves. Each openlistenin a browser holds one of its six connections to the site; for data every page follows,liveshares one WebSocket instead. - An answer of events cannot be kept by
idempotentand is not gzipped. - An HTTP/1.0 client, which cannot read chunks, gets the events with no length, and the connection ends with the stream.
- An option of
sendother thanevent,idandretry, or aretrythat is not a whole number of milliseconds, is a runtime error in the function: the stream ends there. An id or a name is written on one line.
print(events((send) => send("x"), { type: "text/csv", status: 202 }).headers)print(events((send) => send("x"), { headers: { "x-accel-buffering": "yes" } }).headers){ "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "no" }{ "content-type": "text/event-stream", "cache-control": "no-cache", "x-accel-buffering": "yes" }Errors
A first argument that is not a function, options that are not a map, a type that is
not text, and a status outside 200 to 599 or with no body are runtime errors, not
error values.
print(events({ done: 1 }))line 1: `events` takes a function that sends, as in events((send) => send("...")), got mapprint(events((send) => send("x"), { status: "200" }))line 1: `events`: status must be an int from 200 to 599, got "200"Chaining
send is a function, so it takes the end of a pipeline, and http.stream can be
handed it to pass another server's events straight on:
route GET /ticker = events((send) => { for q in quotes() { q | send } })route POST /ask = events((send) => http.stream("https://api.example.com/v1/chat", send, { body: { prompt: request.json.prompt, stream: true }, headers: { authorization: `Bearer ${env("API_KEY")}` },}))See also
stream, listen, receive, http.stream, live
Thread 0 posts
On events(): questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.
Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo