用 abort 中断 AI 流式请求,我之前做错了

"停止生成"这个按钮,看着简单,我第一版做得是错的------点了停止,前端是不显示了,但请求还在后台跑,后端还在生成,token 照烧。后来认真用 AbortController 重做了一遍,把正确姿势和坑记下来。

错误示范:只 cancel reader

我最早是这么写的:

scss 复制代码
// 错误!
reader.cancel()

reader.cancel() 确实停止了前端读流,但底层的 HTTP 请求不一定真的断,后端可能还在往一个没人读的连接里写。看着停了,其实没省。

正确做法:AbortController

把 signal 传给 fetch,中断时调 abort(),整个请求(包括底层连接)才真断:

php 复制代码
const controller = new AbortController()
const res = await fetch('/api/chat', {
  signal: controller.signal,
  method: 'POST',
  body: JSON.stringify({ messages }),
})
// 用户点停止:
controller.abort()

abort() 会让 fetch 抛一个 AbortError,记得 catch 住,别让它当成真错误弹报错给用户:

kotlin 复制代码
catch (e) {
  if (e.name === 'AbortError') return // 用户主动停的,正常
  showError(e)
}

坑一:abort 后状态没清干净

中断后,那条"正在生成"的消息可能停在半句话。我得决定:是保留半句(用户可能就想要这点),还是整条删掉。我选了保留,但在末尾标个"(已停止)",不然用户以为是完整回答。

坑二:abort 和新请求打架

用户停了上一条,马上又发新的。如果没把旧 controller 置空,新请求复用了旧的 signal(已经 aborted),新请求一发就直接被 abort,整个对话框"卡死"。我吃过这亏。每次发新消息都 new 一个全新的 controller:

csharp 复制代码
let controller
function send() {
  controller = new AbortController() // 每次都新建
  ...
}

坑三:组件卸载也该 abort

用户在生成中途切走了页面/关了对话框,组件卸载时也得 abort(),不然请求成了野请求,回调还可能往已销毁的组件里 set state 报警告。在卸载钩子里统一收尾。

没做到位的

abort 我只断了前端,后端是否真的因为连接断开就停止计费,得看后端实现。理想是前端 abort 时再显式发个取消信号给后端,双保险。这块我跟后端还没对齐,暂时只信连接断开这一层。

模型用的讯飞 MaaS,现成 API,连接断了那边的计费逻辑省心,前端把中断这套做严谨就行。你们 abort 还遇到过啥坑,评论区交流。

相关推荐
onething3652 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 5 —— SSE 流式输出 + 打字机效果
人工智能·后端·全栈
一个做软件开发的牛马2 小时前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
码事漫谈2 小时前
AI 编程的「三体」架构:OpenSpec + Superpowers + GStack 如何让一个开发者撑起整个研发团队
后端
吃饱了得干活2 小时前
深入解析 OpenFeign:从重试、拦截到负载均衡的全维度实践
后端
onething3652 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 6 —— 业务完善 + 会话消息预览
人工智能·后端·全栈
BingoGo2 小时前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack2 小时前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
IT_陈寒3 小时前
SpringBoot自动配置的坑,我爬了三天才出来
前端·人工智能·后端
ServBay14 小时前
打通 AI 编程本地运维边界,利用 MCP 协议简化环境与服务管理
后端·ai编程·mcp