You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何等待所有并行Promise完成?前端Token刷新去重优化

解决并发请求下重复Token刷新的优雅方案

核心思路是全局共享一个刷新Token的Promise实例,让所有并发请求复用同一个刷新操作,彻底避免重复发起刷新请求,同时不需要依赖轮询式的等待逻辑。

实现步骤

  1. 全局维护一个变量存储当前正在进行的刷新Promise,初始为null
  2. 当检测到Token过期需要刷新时:
    • 如果已有刷新Promise存在,直接返回该Promise,让所有并发请求等待同一结果
    • 如果没有,则发起刷新请求,将Promise赋值给全局变量,请求完成后重置该变量

代码示例

// 全局变量:存储当前正在进行的Token刷新Promise
let ongoingRefresh = null;

async function refreshAccessToken() {
  // 复用已存在的刷新请求
  if (ongoingRefresh) {
    return ongoingRefresh;
  }

  try {
    // 发起刷新请求,将Promise赋值给全局变量
    ongoingRefresh = fetch('/api/refresh-token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${localStorage.getItem('refreshToken')}`
      }
    }).then(async res => {
      if (!res.ok) throw new Error('Token刷新失败');
      const { accessToken } = await res.json();
      localStorage.setItem('accessToken', accessToken);
      return accessToken;
    }).finally(() => {
      // 无论成功失败,重置全局变量,允许后续刷新请求
      ongoingRefresh = null;
    });

    return ongoingRefresh;
  } catch (err) {
    // 处理刷新失败逻辑,比如跳转登录页
    console.error('刷新Token出错:', err);
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    window.location.href = '/login';
    throw err;
  }
}

// 请求拦截器示例(适配Axios或原生fetch)
async function authorizedRequest(url, options = {}) {
  let accessToken = localStorage.getItem('accessToken');
  const isExpired = checkTokenExpiration(accessToken); // 自行实现Token过期判断

  if (isExpired) {
    // 等待刷新完成,获取新Token
    accessToken = await refreshAccessToken();
  }

  // 携带Token发起请求
  return fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${accessToken}`
    }
  });
}

// 辅助函数:判断Token是否过期
function checkTokenExpiration(token) {
  if (!token) return true;
  const payload = JSON.parse(atob(token.split('.')[1]));
  return Date.now() >= payload.exp * 1000;
}

方案优势

  • 完全基于Promise异步特性,没有setTimeout轮询的hack逻辑,代码更直观易维护
  • 所有并发请求共享同一个刷新操作,不会重复发起刷新请求,避免资源浪费
  • 自动处理刷新成功/失败后的状态重置,不影响后续请求

关于yield的疑问

yield是Generator函数的语法,虽然配合co库也能实现异步流程控制,但现在async/await已经是JavaScript异步编程的标准方案,语法更简洁直观,完全能满足需求,不需要再使用yield的方式。

内容的提问来源于stack exchange,提问作者kguzek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 20:02:49