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

移动端Web用户无操作时维持Server-Sent Events(Azure/fetch-event-source)连接

解决移动端SSE连接后台/失焦后异常的方案

核心原因

移动端Chrome和Safari在页面失焦、进入后台时,会大幅节流定时器、限制网络请求,openWhenHidden仅能解决桌面端非活跃标签页的问题,无法突破移动端的系统级限制。


具体解决办法

1. 自定义重试逻辑,脱离库内置重试

放弃依赖fetch-event-source的内置重试,手动实现重试控制,确保重试定时器尽可能不被系统节流:

let sseRetryTimer = null;
let wakeLock = null;

const connectSSE = async () => {
  // 尝试申请唤醒锁,减少定时器被节流的概率
  if ('wakeLock' in navigator) {
    try {
      wakeLock = await navigator.wakeLock.request('screen');
    } catch (err) {
      console.log('唤醒锁申请失败:', err.message);
    }
  }

  try {
    await fetchEventSource('/your-sse-endpoint', {
      openWhenHidden: true,
      onopen: (res) => {
        console.log('connection made');
        // 连接成功后清除重试定时器
        if (sseRetryTimer) clearTimeout(sseRetryTimer);
      },
      onmessage: (event) => {
        // 处理业务消息
      },
      onerror: (err) => {
        console.error('SSE异常:', err);
        // 30秒后重试
        sseRetryTimer = setTimeout(connectSSE, 30000);
      },
      onclose: () => {
        // 连接主动关闭时触发重试
        sseRetryTimer = setTimeout(connectSSE, 30000);
      }
    });
  } catch (err) {
    console.error('SSE连接失败:', err);
    sseRetryTimer = setTimeout(connectSSE, 30000);
  } finally {
    // 释放唤醒锁
    if (wakeLock) {
      wakeLock.release();
      wakeLock = null;
    }
  }
};

// 初始化连接
connectSSE();

2. 结合页面可见性API补全后台逻辑

监听页面可见状态变化,在后台时主动兜底重试逻辑,避免系统完全阻塞定时器:

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // 页面后台时,检查是否已有重试定时器,无则立即设置
    if (!sseRetryTimer) {
      sseRetryTimer = setTimeout(connectSSE, 30000);
    }
    // 再次尝试申请唤醒锁
    if ('wakeLock' in navigator && !wakeLock) {
      navigator.wakeLock.request('screen').catch(err => {
        console.log('后台唤醒锁申请失败:', err.message);
      });
    }
  } else {
    // 页面回到前台时,立即触发连接,清除旧定时器
    if (sseRetryTimer) {
      clearTimeout(sseRetryTimer);
      connectSSE();
    }
    // 释放唤醒锁
    if (wakeLock) {
      wakeLock.release();
      wakeLock = null;
    }
  }
});

3. iOS Safari 专属 workaround:静音音频保活

iOS Safari对后台页面的限制更严格,可通过播放循环静音音频让浏览器认为页面处于活跃状态,减少节流:

import { useEffect } from 'react';

const useSilentAudioKeepAlive = () => {
  useEffect(() => {
    const audio = new Audio('/silent.mp3');
    audio.loop = true;
    audio.muted = true;

    // iOS 需要用户交互触发音频播放,所以在首次触摸时启动
    const triggerPlay = () => {
      audio.play().catch(err => {
        console.log('静音音频播放失败:', err.message);
      });
      document.removeEventListener('touchstart', triggerPlay);
    };
    document.addEventListener('touchstart', triggerPlay);

    return () => {
      audio.pause();
      document.removeEventListener('touchstart', triggerPlay);
    };
  }, []);
};

// 在组件中使用
const YourComponent = () => {
  useSilentAudioKeepAlive();
  // ...其他逻辑
};

4. 后端配合:添加心跳机制

后端每10-15秒发送一次心跳消息(比如event: ping\ndata: \n\n),避免移动端浏览器因长时间无数据主动断开连接,同时前端可通过心跳检测连接存活状态。


注意事项

  • 唤醒锁并非所有设备都支持,需做好降级处理
  • 静音音频文件需是真正无声的短音频(比如1秒左右),避免占用资源
  • 移动端后台限制是系统级的,无法100%保证连接不中断,但上述方案可大幅提升稳定性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 10:44:56