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

