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

相关推荐
星栈1 小时前
用 Rust 写 Agent 服务 -- adk-rust 上手记
后端·agent
杨运交1 小时前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring
LucianaiB2 小时前
我用豆包工作 Seed-2.1-pro,复刻了 QQ 时代爆红的魔术图片
后端
码事漫谈2 小时前
智谱 ZCode 静默上传 Git 历史:48 小时信任危机复盘
后端
掘金码甲哥2 小时前
当对话模型遇上向量模型,vllm production stack 又该如何应对?
后端
传奇开心果编程3 小时前
【springboot基础语法学与练】第 1 课:从零开始
java·spring boot·后端·学习
IT_陈寒3 小时前
Java线程池用错参数,我的服务居然悄悄崩溃了
前端·人工智能·后端
光影少年3 小时前
Express 中间件原理
后端·node.js·express
aramae4 小时前
MySQL内置函数(7)
开发语言·笔记·后端·mysql·其他
徐小夕5 小时前
存量.doc 文档怎么办?JitWord 开源转换库打通旧文档到在线协同全链路
后端·架构·github