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

React中如何查看setTimeout队列函数?休眠后能否触发过期的setTimeout?

解决设备休眠后JWT过期未触发登出的问题

关于查看setTimeout队列

浏览器没有提供直接访问setTimeout任务队列的API,React也没有封装这类能力。setTimeout的回调由浏览器事件循环管理,无法直接枚举或查看队列中的待执行函数。

处理设备休眠导致的超时逻辑失效

依赖setTimeout的计时逻辑在设备休眠时会彻底失效——休眠期间系统时钟暂停,定时器不会继续推进计时,唤醒后才会继续剩余时长的计数,但此时实际令牌早已过期,定时器却仍未触发。更可靠的方案是放弃被动等待定时器触发,改为主动校验令牌状态:

方案1:监听设备唤醒/页面可见事件+主动校验

浏览器提供visibilitychange事件(页面从隐藏切换到显示时触发,包含设备唤醒场景),结合online事件(休眠唤醒常伴随网络重连),在这些事件触发时主动检查令牌是否过期:

useEffect(() => {
  const checkTokenValidity = () => {
    const token = localStorage.getItem('jwt');
    if (!token) return;
    
    try {
      // 前端解析JWT payload(仅用于获取过期时间,不代表令牌服务端有效)
      const payload = JSON.parse(atob(token.split('.')[1]));
      const currentTimestamp = Date.now() / 1000;
      
      if (payload.exp < currentTimestamp) {
        // 令牌已过期,执行登出逻辑
        localStorage.removeItem('jwt');
        // 补充你的登出操作:比如跳转登录页、更新全局状态等
        window.location.href = '/login';
      }
    } catch (err) {
      // 令牌格式异常,直接清理
      localStorage.removeItem('jwt');
    }
  };

  // 绑定事件监听
  document.addEventListener('visibilitychange', checkTokenValidity);
  window.addEventListener('online', checkTokenValidity);
  // 组件挂载时先执行一次校验
  checkTokenValidity();

  // 组件卸载时移除监听
  return () => {
    document.removeEventListener('visibilitychange', checkTokenValidity);
    window.removeEventListener('online', checkTokenValidity);
  };
}, []);

方案2:用setInterval定期校验

如果需要持续监控令牌状态,可以用setInterval定期检查,比如每分钟执行一次校验:

useEffect(() => {
  const intervalId = setInterval(() => {
    const token = localStorage.getItem('jwt');
    if (!token) return;

    const payload = JSON.parse(atob(token.split('.')[1]));
    const currentTimestamp = Date.now() / 1000;
    
    if (payload.exp < currentTimestamp) {
      localStorage.removeItem('jwt');
      clearInterval(intervalId);
      // 执行登出操作
      window.location.href = '/login';
    }
  }, 60000); // 每分钟检查一次

  return () => clearInterval(intervalId);
}, []);

关于“运行已超时的setTimeout”

设备休眠期间,setTimeout的计时是暂停的,唤醒后它只会继续剩余时长的计数,不会感知到实际时间已超过令牌过期时间。而且你无法从外部直接访问队列中“应该超时”的定时器回调——除非你在创建定时器时手动保存了回调函数和过期时间:

// 登录时的逻辑
const handleLogin = () => {
  const token = '获取到的JWT令牌';
  localStorage.setItem('jwt', token);
  
  const payload = JSON.parse(atob(token.split('.')[1]));
  const expireTimestamp = payload.exp * 1000;
  const timeUntilExpire = expireTimestamp - Date.now();
  
  // 全局存储回调和过期时间
  window.tokenExpireCallback = () => {
    localStorage.removeItem('jwt');
    window.location.href = '/login';
  };
  window.tokenExpireTime = expireTimestamp;

  // 正常初始化定时器
  setTimeout(window.tokenExpireCallback, timeUntilExpire);
};

// 监听唤醒事件的逻辑
useEffect(() => {
  const checkAndRunCallback = () => {
    if (!window.tokenExpireCallback || !window.tokenExpireTime) return;
    
    if (Date.now() > window.tokenExpireTime) {
      window.tokenExpireCallback();
      // 执行后清理全局变量
      delete window.tokenExpireCallback;
      delete window.tokenExpireTime;
    }
  };

  document.addEventListener('visibilitychange', checkAndRunCallback);
  window.addEventListener('online', checkAndRunCallback);

  return () => {
    document.removeEventListener('visibilitychange', checkAndRunCallback);
    window.removeEventListener('online', checkAndRunCallback);
  };
}, []);

但这种方式依赖全局变量,容易引发内存泄漏或状态不一致问题,优先级低于主动校验方案。

总结

  1. 无法直接查看浏览器的setTimeout任务队列;
  2. 不要依赖setTimeout处理JWT过期逻辑,设备休眠会导致计时失效;
  3. 最优方案是通过visibilitychange、online事件主动校验令牌状态,或用setInterval定期检查;
  4. 若坚持使用定时器相关逻辑,需手动保存回调和过期时间,唤醒时对比时间手动执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:50:26