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

RNEventSource处理SSE陷入无限循环,仅接收attempt:1问题排查

Expo React Native项目SSE无限循环问题

问题现象

后端已通过Postman验证可正常流式传输SSE数据,但前端使用RNEventSource时陷入无限循环,反复接收相同响应,日志如下:

LOG  Open SSE connection.
LOG  Stream Data: {"attempt": 1, "run_id": "1efb7463-0ff8-60c3-bd10-17ccc210899f"}
WARN  Unexpected Data: {"run_id":"1efb7463-0ff8-60c3-bd10-17ccc210899f","attempt":1}
LOG  Stream Data: {"attempt": 1, "run_id": "1efb7463-0ff8-60c3-bd10-17ccc210899f"}
WARN  Unexpected Data: {"run_id":"1efb7463-0ff8-60c3-bd10-17ccc210899f","attempt":1}
...

前端代码

const askDogy = async (request) => {
  const url = `${assitantBaseUrl}/threads/${threadId}/runs/stream`
  const requestBody = JSON.stringify({
    assistant_id: assistantID,
    input: {
      messages: [{ role: 'user', content: request }],
    },
    stream_mode: ['messages'],
  })

  const options = {
    body: requestBody,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'text/event-stream',
    },
  }

  eventSourceRef.current = new RNEventSource(url, options)

  eventSourceRef.current.addEventListener('open', () => {
    console.log('Open SSE connection.')
  })

  eventSourceRef.current.addEventListener('message', (event) => {
    console.log('Raw Event:', event)
    try {
      const data = JSON.parse(event.data)
      console.log('Parsed Data:', data)

      if (data.content) {
        console.log('AI Reply Content:', data.content)
        setChatResponse('Bot', data.content, true)
      } else {
        console.warn('Unexpected Data:', data)
      }
    } catch (error) {
      console.error('Error parsing event data:', error)
    }
  })

  eventSourceRef.current.addEventListener('error', (error) => {
    console.error('Stream error:', error)

        if (error.status === 409) {
          console.log('Conflict detected, creating a new thread...')
          // Create a new thread and retry
          tempThreadId = await createThread()
          if (!tempThreadId)
            throw new Error('Failed to create a new thread after conflict')
          setThreadId(tempThreadId)

          // Retry the stream with the new thread ID
          console.log('Retrying with new thread ID:', tempThreadId)
          await askDogy(request) // Recursive call with the new thread
        } else {
          handleStopStream()
        }
  })
}

调试观察

  • 反复接收attempt:1的相同数据,无增量更新
  • 后端应发送的后续数据未出现在前端日志中

疑问

  • RNEventSource是否存在已知问题或React Native兼容性问题?
  • 在RNEventSource中处理增量更新是否需要特殊操作?
  • 有没有其他可用于React Native/Expo处理SSE的npm包?

已尝试的解决方法

  • 验证后端符合SSE规范
  • 确保RNEventSource初始化包含正确的请求头和方法
  • 曾添加后端故障时的最大重试和超时逻辑(现已移除)
  • 通过Postman测试后端流式传输正常

问题解答

  1. RNEventSource的兼容性问题
    RNEventSource存在一些已知的兼容性问题,尤其是在Expo项目中:
  • 部分版本对POST请求的SSE支持不完善,会出现连接重复建立、自动重连逻辑异常的情况,导致重复接收初始响应数据。
  • 与Expo的网络层可能存在冲突,触发无限重连循环。
  1. RNEventSource处理增量更新的注意事项
  • 严格遵循SSE规范:后端输出的每个数据块必须以data:前缀开头,单条数据换行分隔,完整事件以两个连续换行结束;多段数据需每段添加data:,最后用两个换行收尾。
  • 避免重复创建实例:每次发起新请求前,必须关闭之前的连接(eventSourceRef.current?.close()),否则旧连接未释放会导致重复接收数据。
  • 控制自动重连:RNEventSource默认会在连接断开后自动重连,若后端未正确发送结束信号,会引发无限重连。建议在收到完整响应后主动调用close(),或通过options设置reconnectInterval: 0禁用自动重连。
  • 优化请求头:添加Cache-Control: no-cache、Connection: keep-alive头,防止缓存或连接被提前中断。
  1. 替代的SSE处理包
  • react-native-sse:纯JS实现,兼容Expo和裸React Native,支持POST请求与自定义头,稳定性优于RNEventSource,无需原生依赖。
  • eventsource-polyfill:可作为浏览器EventSource的polyfill在React Native中使用,需适配网络请求配置。
  • Axios流式请求:若后端支持,可使用Axios的responseType: 'stream'手动解析SSE格式数据,适合需要高度自定义的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:04:51