已开启
fix(http): 修复 token 刷新失败时的 Promise 悬挂问题 #1
忘语创建于 2025年10月30日
fix(http): 修复 token 刷新失败时的 Promise 悬挂问题 #1
已开启
忘语创建于 2025年10月30日
忘语
忘语
2025年10月30日

🐛 问题描述

src/http/http.ts 的 token 刷新逻辑中存在严重的 Promise 悬挂和内存泄漏问题:

原问题:

  • 当 token 过期触发刷新时,并发请求会被添加到 taskQueue 队列等待
  • 如果刷新成功,队列中的请求会被重新执行 ✅
  • 如果刷新失败,队列只是被清空,但这些 Promise 没有被 reject ❌
  • 导致调用方永远等待这些请求,造成内存泄漏和糟糕的用户体验

影响范围:

  • 所有在 token 刷新期间发起的并发请求
  • 当刷新失败时,这些请求会永久处于 pending 状态
  • 用户界面可能出现 loading 状态无法消失的情况

✨ 解决方案

  1. 优化任务队列数据结构

    • 原来:let taskQueue: (() => void)[] = []
    • 修改为:let taskQueue: Array<{ resolve: () => void, reject: (reason?: any) => void }> = []
    • 同时存储每个请求的 resolve 和 reject 方法
  2. 正确处理刷新失败场景

    • 添加 taskQueue.forEach(task => task.reject(refreshErr))
    • 确保刷新失败时,所有等待的 Promise 都能被正确拒绝
    • 避免 Promise 悬挂问题
  3. 保持原有功能不变

    • 刷新成功时仍然正常重试所有请求
    • 不影响现有的错误处理和用户提示逻辑

📊 技术细节

修改文件: src/http/http.ts

核心改动:

  • 第 11 行:优化队列类型定义
  • 第 43-46 行:将 resolve 和 reject 都存入队列
  • 第 66 行:刷新成功时调用 task.resolve()
  • 第 87 行:新增刷新失败时调用 task.reject(refreshErr)

✅ 测试建议

  1. 模拟 token 过期场景,发起多个并发请求
  2. 让 refreshToken 接口返回失败
  3. 验证所有等待的请求都能正确收到错误响应
  4. 确认 UI 层的 loading 状态能正常关闭

🎯 预期效果

  • ✅ 修复 Promise 悬挂问题
  • ✅ 避免内存泄漏
  • ✅ 改善用户体验
  • ✅ 保持代码健壮性
likedislike
合并受阻