用户反馈"页面一直转圈"。排查发现:导出报表接口响应 20s,但前端统一超时 15s,直接断了。
加大超时?简单但粗暴——所有接口都变成 30s 超时,普通查询也要等 30s 才报错,体验更差。我让 AI 帮我设计了一个动态超时策略,根据接口类型自动适配。
问题分析
接口类型 实际响应时间 统一 15s 超时的问题 ────────── ────────── ────────────────── 普通查询 200ms-2s 正常 列表分页 500ms-5s 正常 复杂查询 3s-8s 正常 导出(小数据量) 5s-15s 偶尔超时 导出(大数据量) 15s-60s 必然超时 ❌ 文件上传 10s-120s 必然超时 ❌ 批量操作 5s-30s 经常超时 ❌不同接口需要不同的超时时间,但又不想每个接口调用时手动传。
AI 设计的方案:3 层超时策略
优先级(高→低): Layer 1: 接口级覆盖(单个接口指定) ↓ 没指定则走 Layer 2: 场景级默认(按 API 类型分类) ↓ 没匹配则走 Layer 3: 全局默认(15s)// 超时策略配置constTIMEOUT_STRATEGY={// 全局默认default:15_000,// 场景级scenes:{query:15_000,// 普通查询list:20_000,// 列表请求mutation:20_000,// 增删改export:120_000,// 导出upload:180_000,// 上传batch:60_000,// 批量操作},// 接口级覆盖(特殊接口)overrides:{'/report/export/full':300_000,// 全量报表,5min'/goods/batch-import':180_000,// 商品批量导入},}核心实现:请求拦截器
constgetTimeout=(config:AxiosRequestConfig):number=>{consturl=config.url||''// Layer 1: 接口级覆盖(config 里手动传的)if(config.timeout)returnconfig.timeout// Layer 2: 接口路径匹配constoverride=Object.entries(TIMEOUT_STRATEGY.overrides).find(([path])=>url.includes(path))if(override)returnoverride[1]// Layer 3: 场景推断constscene=inferScene(config)returnTIMEOUT_STRATEGY.scenes[scene]||TIMEOUT_STRATEGY.default}constinferScene=(config:AxiosRequestConfig):string=>{const{url='',method,responseType}=config// 导出类(responseType 为 blob 或 URL 包含 export)if(responseType==='blob'||url.includes('export'))return'export'// 上传类(Content-Type 包含 multipart)if(config.headers?.['Content-Type']?.includes('multipart'))return'upload'// 批量操作(URL 包含 batch)if(url.includes('batch'))return'batch'// 列表类(URL 包含 list/page 且为 GET)if(method==='get'&&/\/(list|page)/.test(url))return'list'// 增删改if(['post','put','delete'].includes(method||''))return'mutation'// 默认查询return'query'}关键设计:不需要改任何现有代码。现有的接口调用完全不用动,拦截器自动推断超时时间。
使用方式
// 普通使用——自动推断(大部分场景)exportconstgetStoreList=(params)=>request.get('/store/list',{params})// → 自动匹配 list 场景,20s 超时// 导出——自动识别exportconstexportReport=(params)=>request.get('/report/export',{params,responseType:'blob'})// → 自动匹配 export 场景,120s 超时// 特殊接口——手动覆盖exportconstexportFullReport=(params)=>request.get('/report/export/full',{params,responseType:'blob',timeout:300_000})// → Layer 1 手动指定,5minAI 帮我发现的额外优化点
对话过程中,AI 还建议了两个我没想到的优化:
1. 超时 ≠ 报错,给用户进度感知
// 对于已知耗时长的操作,显示进度提示constexportWithProgress=async(params)=>{consthide=message.loading('正在生成报表,数据量较大请耐心等待...',0)try{returnawaitrequest.get('/report/export',{params,responseType:'blob',})}finally{hide()}}2. 超时后的重试策略不同
// 普通查询超时:静默重试 1 次// 导出超时:提示用户"数据量大,是否继续等待?"// 上传超时:不重试,提示检查网络consthandleTimeout=(config:AxiosRequestConfig,error:AxiosError)=>{constscene=inferScene(config)switch(scene){case'query':case'list':returnretryOnce(config)// 静默重试case'export':returnconfirmRetry('报表生成中,是否继续等待?',config,300_000)case'upload':message.error('上传超时,请检查网络后重试')returnPromise.reject(error)default:message.error('请求超时,请稍后重试')returnPromise.reject(error)}}效果
优化前: ├── 导出接口:超时率 35%(用户点击后无响应 → 以为卡死 → 重复点击) ├── 批量操作:超时率 15% ├── 用户投诉:每周 8-10 条"页面卡死" 优化后: ├── 导出接口:超时率 2%(剩余是真的后端慢需要优化) ├── 批量操作:超时率 0% ├── 用户投诉:每周 3-4 条(减少 60%) ├── 额外收益:定位到 5 个真正需要后端优化的接口什么时候需要动态超时
需要的信号: ├── 有导出/上传等长耗时接口 ├── 经常收到"超时"报错 ├── 不同接口耗时差异大(200ms vs 60s) └── 统一超时值怎么调都不合适 不需要的: ├── 所有接口 <5s 就能响应 ├── 后端保证响应时间 SLA └── 项目小,接口少于 20 个💬 你们项目的接口超时是怎么设置的?有没有遇到过"超时导致体验差"的问题?
🔗完整 Skills 源码已开源:github.com/sleepyccat/ai-native-workflow,欢迎 Star ⭐ 和 PR。