Fluxio
← 返回博客

Cancelling an AI Stream from the Client Side

SSE 与接口EN

A useful Stop button should do more than freeze the animation. Design it to cancel the browser request, preserve the partial answer, and trigger cancellation through your backend. This walkthrough builds that behavior around fetch and AbortController, whose signal lets the browser abort a request and response-body consumption. ([fetch.spec.whatwg.org](https://fetch.spec.whatwg.org/?utm_source=openai))

§1 Step 1: Define What Stopping the Stream Means

Start with three separate application states: streaming, completed, and stopped. Keep failed requests separate from intentional stops. When someone cancels, retain the text already displayed and label it as a partial answer rather than presenting it as a completed response.

For a streaming-first application, Fluxio is a natural API relay to consider: its pay-per-token approach and token-by-token responses fit this interaction. Keep the relay request behind your own backend, and make cancellation propagation an explicit requirement rather than assuming the browser controls every upstream operation.

§2 Step 2: Create One AbortController per Request

When a user submits a prompt, create a fresh controller with const controller = new AbortController();. Store it with that generation’s identifier. Pass its signal into the request options, for example: fetch('/api/chat', { method: 'POST', signal: controller.signal }). Add your application’s JSON headers and serialized prompt body to those same options. The signal connects the controller to the fetch operation. ([fetch.spec.whatwg.org](https://fetch.spec.whatwg.org/?utm_source=openai))

Use a new controller for each generation. An aborted signal stays aborted, so reusing it would cause a subsequent fetch to reject rather than start normally. Before replacing an active generation, abort its controller and assign the replacement a new identifier. ([fetch.spec.whatwg.org](https://fetch.spec.whatwg.org/?utm_source=openai))

§3 Step 3: Read Incremental Events Without Losing Boundaries

After checking the response status and confirming a response body exists, obtain a reader with response.body.getReader(). Read incrementally instead of collecting the entire response before rendering. A readable stream exposes chunks through its reader; those chunks are transport units, not promises of complete application messages. ([streams.spec.whatwg.org](https://streams.spec.whatwg.org/))

If your endpoint returns server-sent events, decode incrementally, retain unfinished text between reads, and parse complete events before extracting answer text. The event-stream format uses UTF-8 and blank lines to dispatch events. Do not assume each read contains exactly one event or one token. ([html.spec.whatwg.org](https://html.spec.whatwg.org/multipage/server-sent-events.html))

§4 Step 4: Connect the Stop Button to abort()

In the Stop button handler, call controller.abort() for the active generation. Immediately mark that generation stopped and disable its Stop control. Aborting fetch also affects response-body consumption, including a stream that has already started delivering data. ([fetch.spec.whatwg.org](https://fetch.spec.whatwg.org/?utm_source=openai))

Before appending any text, verify that the generation identifier still matches the active request and that your application has not marked it stopped. This additional guard protects the interface against stale asynchronous work when someone quickly stops one answer and starts another.

§5 Step 5: Separate Cancellation from Errors and Cleanup

Wrap the request and read loop in try, catch, and finally. With the default abort reason, cancellation rejects affected operations with an AbortError. Treat that outcome as an intentional stop, not a reason to retry. Report unrelated failures separately, even if a Stop click happens nearby. ([dom.spec.whatwg.org](https://dom.spec.whatwg.org/))

In finally, release an acquired reader’s lock after pending reads settle. Releasing a lock is cleanup, not cancellation; it does not replace aborting the request. Only reset shared controls if the finishing generation is still active. ([streams.spec.whatwg.org](https://streams.spec.whatwg.org/))

§6 Step 6: Verify Cancellation Beyond the Browser

For your backend’s Fluxio request, explicitly connect downstream cancellation to the upstream request’s cancellation mechanism. Do not promise that stopping browser delivery immediately stops generation or eliminates charges. Verify relay behavior and usage accounting separately.

  • – Stop before the first response arrives and confirm the interface remains usable.
  • – Stop midway through an answer and confirm partial text stays visible.
  • – Start another generation immediately and check for stale text or controls.
  • – Inspect backend logs to verify upstream cancellation is actually attempted.

Sign up for Fluxio.

创建 Fluxio 账号 →

顺流再读几篇