用 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 还遇到过啥坑,评论区交流。

相关推荐
Zane19944 分钟前
调用了 async 函数却没执行?一文讲透协程、event loop 与 await
后端·python
Zane19949 分钟前
HashMap 为什么要在长度16、容量必须是2的幂这些细节上较劲
java·后端
掘金者阿豪38 分钟前
若依启动突然报 Redis MISCONF?一次从应用报错到磁盘爆满的完整排查记录
后端
boooooooom2 小时前
尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
前端·后端·llm
2601_963870183 小时前
【计算机毕业设计】基于Vue+Spring Boot的助农电商系统的设计与实现
spring boot·后端·课程设计
sun༒3 小时前
Spring @Scheduled 定时任务详解:Cron表达式、执行顺序、优先级与并行调度
java·后端·spring
yuhaiqiang3 小时前
从这两件事就能看出 vibecoding 距离专业作品差距有多大?AI 能抹平技术,但抹不平品味 !
前端·后端·程序员
野生技术架构师3 小时前
Spring Boot 实现数据脱敏:自定义注解 + Jackson 序列化器
java·spring boot·后端
明月_清风5 小时前
vLLM 深度实战:2026 年生产级 LLM 推理引擎完全指南
前端·后端·ai编程