Sending the page updates while the work is still running
2026年6月18日
chunk() is apply() that does not wait. Call it as often as you like during a route and each call reaches the browser immediately, over the response that is still open.
The pattern it replaces is familiar and unpleasant: a long job, a spinner, and either a page that appears frozen or a second channel built purely to report progress. A job queue, a status table, a poll every second, a websocket for something that is not really realtime. All of that to say "step 4 of 16".
Since the SPA engine already understands DOM commands, progress does not need a channel of its own. The same command arriving repeatedly is a progress bar:
route async POST import {
foreach ($rows AS $i => $row){
import::row($row)
chunk(inner: ['#status' => ($i + 1).' of '.count($rows)])
}
return apply(inner: ['#status' => 'Done'])
}
The last one is an ordinary apply(), so the request ends the way any other does.
Two things worth knowing. Bulky updates can pass state: false so the engine does not re-sync history on every chunk, with one final apply() syncing once; that is what makes a token-by-token stream cheap. And this is the same channel a streamed model answer uses, which is why an AI response in Phlo needs no special client code, just chunk() per token.
Engine 1.0. See the Views chapter.