调AI接口总崩?前端错误处理不该只是catch一下
先说结论
try-catch只能捕获同步或await中的错误,网络中断、超时、JSON解析异常需要单独处理
友好的用户提示不是简单弹个框,要区分错误类型并给出可操作的建议
降级方案和重试机制是AI功能可用性的最后一道防线
从一线开发者真实的踩坑经历出发,聊AI接口异常处理的常见盲区,以及如何设计一套更健壮的容错方案。
AI接口调用,90%的错误都没被正确处理
前端调AI接口,最怕什么?
不是模型回答跑偏,而是接口突然崩了。用户正等着结果,你这边控制台一行红字,页面卡住不动。然后用户反复点按钮,请求堆成山,后台炸得更厉害。
更现实的是,很多项目的错误处理只写了一个catch块,然后console.error一输,alert一弹,完事。
但真正的战场远不止这些。
你以为try-catch就够了?这些场景它抓不住
先看一个最常见的代码结构:
asyncfunctioncallAI(prompt){constres=awaitfetch('/api/generate',{...});constdata=awaitres.json();returndata;}表面上看,await后面的错误会被外层try-catch捕获。但实际线上会漏掉这些:
- 网络中断:fetch本身可能直接抛出TypeError: Failed to fetch,但如果在用AbortController,超时异常又是另一种。
- JSON解析失败:如果后端返回了200但内容不是合法JSON,res.json()会抛SyntaxError。
- HTTP非200状态:fetch只有在网络故障时reject,4xx、5xx并不会主动抛异常,你得自己检查res.ok。
- 跨域错误:CORS配置不当,浏览器直接不给你响应体,异常信息也很有限。
所以一个稍微健壮的写法,至少要把这些拆开:
asyncfunctionsafeCallAI(prompt,signal){letres;try{res=awaitfetch('/api/generate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({prompt}),signal});}catch(e){if(e.name==='AbortError'){return{error:'请求已取消',type:'cancel'};}return{error:'网络连接失败,请检查网络',type:'network'};}if(!res.ok){// 尝试读取后端错误信息letbody;try{body=awaitres.json();}catch{body={};}return{error:body.message||`服务端错误(${res.status})`,type:'server'};}letdata;try{data=awaitres.json();}catch{return{error:'数据解析异常',type:'parse'};}returndata;}友好提示不是“网络错误”,要分场景给建议
很多项目统一弹一个“网络错误,请稍后重试”。但用户看到后只会困惑:是我的网坏了?还是你们服务器挂了?要不要现在重试?
更好的做法是区分错误类型,给出具体建议:
- 网络断开:提示“当前无网络连接,请检查Wi-Fi或移动数据”,并禁用重试按钮,等网络恢复后自动检测。
- 请求超时:提示“AI响应较慢,已超过等待时间,您可以稍后重试或缩短输入内容”。
- 服务端限流:提示“服务繁忙,请稍后再试”,并显示等待倒计时。
- 模型内容审核驳回:提示“输入内容不符合安全规范,请修改后重试”。
同时要避免让用户陷入重复点击的循环。我倾向于在错误UI上设置一个“手动重试”按钮,而不是自动无限重试。
降级方案:当AI不可用时,你还有B计划吗?
纯AI功能一旦崩溃,整个页面就废了。这在生产环境是不可接受的。
一个简单的降级思路是:
- 如果AI生成文本失败,可以展示一段预置文案,比如“智能推荐暂时不可用,以下是热门推荐”。
- 如果AI识别图片失败,可以回退到基础OCR或直接让用户手动输入。
- 如果AI对话接口超时,可以让用户先提交工单,后台异步处理后再通知。
降级方案不要求功能完全等价,关键是要让用户不卡住,有路可走。
重试策略:指数退避还是固定间隔?
一旦决定重试,怎么重试?
最粗暴的是失败后立刻重试,这大概率会继续失败,还浪费服务器资源。
稍微好一点的是固定间隔重试,比如每隔2秒重试一次,最多3次。但如果服务器正在重启,2秒可能不够。
更推荐的是指数退避:第一次等待1秒,第二次2秒,第三次4秒,同时加上随机抖动(jitter),避免所有客户端同时重试造成雪崩。
asyncfunctionretryWithBackoff(fn,maxRetries=3,baseDelay=1000){for(leti=0;i<maxRetries;i++){constresult=awaitfn();if(!result.error)returnresult;if(result.type==='server'&&result.status>=500){// 服务端错误才重试constdelay=baseDelay*Math.pow(2,i)+Math.random()*1000;awaitnewPromise(r=>setTimeout(r,delay));}else{// 客户端错误不重试returnresult;}}return{error:'多次重试后仍然失败',type:'retry_exhausted'};}注意:只有5xx类错误才值得重试。4xx类(如400、401、403)重试多少次都没用。
写在最后:错误处理的边界与权衡
上面这套方案看起来完整,但并不是所有项目都值得全量实现。
- 如果你的AI功能只是锦上添花(比如智能推荐),降级到静态内容就够了,不需要重试。
- 如果你的AI功能是核心流程(比如智能客服),那必须做重试+降级+监控告警。
- 如果是内部工具,用户容忍度高,简单提示可能就够了。
我个人更倾向于先区分错误类型,给用户清晰提示,再根据业务重要性决定是否重试。不要一上来就写复杂的重试逻辑,容易引入更多bug。
你说到底是让用户手动刷新,还是自动重试三次后放弃?这个问题没有标准答案,取决于你的用户场景和容忍度。
如果你正在做AI前端对接,可以在评论区聊聊你的方案——是倾向重试到底,还是直接让用户手动操作?
最后留一个讨论点
你会选择在客户端重试三次后放弃,还是直接让用户手动刷新页面?两种策略各自有什么代价?
