KAIROS.WORKSPACE
技术宇宙 / ONLINE
返回文章列表
前端工程intermediate6 分钟

让前端"看得懂"限流:429/503 的退避与文案分工

当接口返回 429 或 503,前端该重试、等待还是提示?讲清按状态码分流、指数退避、Retry-After 利用与"用户可修复 vs 不可修复"的文案设计。

#API#前端#错误处理#限流
排版
字号
行宽
目录 · 5 节

个人站点的评论、点赞、订阅接口都会限流。多数前端把它们一律渲染成“操作失败,请重试”,于是用户对着一个“再试也没用”的按钮反复点击。限流响应的正确处理,是按状态码分流,而不是统一报错。

先看服务端给了什么信号

一个设计良好的接口,不同失败会给不同状态码 + 可选的重试提示:

状态码 含义 前端应做
400 输入无效(用户可修复) 提示改输入,自动重试
429 触发限流(等待可恢复) Retry-After,禁用提交并倒计时,可选自动退避重试
503 服务暂不可用(用户无法修复) 提示稍后再试 / 换渠道,误导用户改输入

Retry-After 头(429/503 常带)是服务端明确告诉你“多久后再来”:

HTTP/1.1 429 Too Many Requests
Retry-After: 30

分流处理

async function submit(payload) {
  const res = await fetch('/api/subscribe', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(8000), // 关键:超时兜底,别让它无限挂着
  });
  const data = await res.json().catch(() => ({}));

  if (res.status === 429) {
    const wait = parseInt(res.headers.get('Retry-After') || '30', 10);
    startCountdown(wait); // 禁用按钮 + 倒计时,防继续点
    return show('操作太频繁,请 ' + wait + ' 秒后再试。');
  }
  if (res.status >= 500) {
    return show(data.message || '服务暂时不可用,请稍后重试。'); // 不背锅给用户输入
  }
  if (!res.ok) {
    return show(data.message || '提交的内容似乎有问题,请检查。'); // 4xx:用户可修复
  }
  return ok(data);
}

要点:

  1. signal: AbortSignal.timeout(...) 是第一道防线——没有它,接口一卡,按钮永久停在“正在提交…”,比任何文案错误都伤体验;
  2. 透传服务端 message 优先于前端硬编码(服务端最懂真实原因);
  3. 429 要消费 Retry-After,禁用 + 倒计时,把“你别再点了”变成可见的确定信息;
  4. 5xx 不翻译成输入错误——这是真实踩过的坑(详见踩坑《订阅接口 503 被前端翻译成了“邮箱格式不对”》)。

要不要自动重试?

  • 幂等请求(GET 拉评论数)+ 5xx/网络错:可指数退避重试,上限 2–3 次;
async function withBackoff(fn, retries = 3) {
  for (let i = 0; i < retries; i += 1) {
    try {
      return await fn();
    } catch (e) {
      if (i === retries - 1) throw e;
      await new Promise((r) =>
        setTimeout(r, 300 * 2 ** i + Math.random() * 100),
      ); // 加抖动
    }
  }
}
  • 非幂等提交(POST 点赞/订阅):不要盲目自动重试,除非服务端支持幂等键。重复点击 + 自动重试 = 重复提交。宁可用户手点一次明确的“重试”。
  • 退避要加抖动(jitter),否则多客户端会在同一时刻同步重试,把刚恢复的服务再打倒。

文案的三分法

给用户的错误信息按“下一步能做什么”分类,而不是按技术原因分类:

  • 你能修的:说清楚改哪里(“昵称不能为空”),聚焦到出错字段;
  • 等会儿就行的:给确定时间锚点(“30 秒后可再试”),别写“稍后再试”这种无信息量话;
  • 你修不了、也不该你扛的:诚实说明是服务端问题 + 给替代渠道(“也可用 RSS 关注”),绝不暗示是用户操作失误。

小结

限流不是一个错误码,是一组需要区别对待的信号:超时要有 AbortSignal、429 要读 Retry-After 并禁用倒计时、5xx 要如实归因、幂等才可退避重试。把“操作失败请重试”拆成这三类可执行提示,接口的限流机制才第一次真正为用户所用,而不是只服务于服务端自保。

Conversation

评论与互动

正在加载评论…

提交后需审核,不会立即公开。

Keep exploring

继续探索