import { fetchEventSource } from '@microsoft/fetch-event-source'
class FetchEventSourceController {
constructor(url, options = {}) {
this.url = url
// 为 options 提供默认值,确保请求可以正常发起
this.options = {
headers: {
Accept: 'application/json', // 默认接受 JSON 格式
'Content-Type': 'application/json', // 默认请求内容类型为 JSON
...options.headers // 允许外部传入自定义 headers
},
body: options.body || null, // 如果外部没有传入 body,默认为 null
method: options.method || 'GET', // 默认为 GET 请求
reconnectInterval: options.reconnectInterval || 2000, // 默认为 2000ms 重连间隔
maxReconnectAttempts: options.maxReconnectAttempts || 15 // 最大重连次数
// handleMessage: options.handleMessage || (() => {}), // 默认 handleMessage 为一个空函数
}
this.reconnectInterval = this.options.reconnectInterval // 重连时间间隔
this.maxReconnectAttempts = this.options.maxReconnectAttempts // 最大重连次数
this.reconnectAttempts = 0 // 记录当前重连尝试次数
this.isReconnecting = false // 是否主动断开
this.eventSource = null // 控制器
this.reconnectTimeout = null // 用于存储定时器,防止重复重连
this.messageQueue = [] // 用于存储待处理的消息队列
this._messageLock = false // 标志是否有消息正在处理
this._handleQueueID = null //队列消息派发计时器
this._heardCheckID = null //心跳包计时器
this.connect()
}
connect() {
const {
headers,
body,
method
// handleMessage
} = this.options
this.eventSource = fetchEventSource(this.url, {
headers, // 使用自定义 headers 或默认值
body, // 使用自定义 body 或默认值
method, // 使用自定义 method 或默认值
onopen: response => {
// console.log('onopen', response); // 也可以调用外部传入的 handleMessage 函数
this._messageLock = false // 队列上锁
this.isReconnecting = false // 队列上锁
this.reconnectAttempts = 0 // 重置重连尝试次数
this.initOpen() // 启动事件队列机制
},
onmessage: event => {
// 将收到的消息入队
this.pushQueue(event.data)
},
onerror: error => {
// console.log('onerror', error); // 也可以调用外部传入的 handleMessage 函数
this.handleReconnect()
},
onclose: () => {
// console.log('onclose'); // 也可以调用外部传入的 handleMessage 函数
this.handleReconnect()
}
})
}
initOpen(event, fCallBack) {
this._handleQueueID && clearInterval(this._handleQueueID)
this._handleQueueID = setInterval(this.handleQueue, 100)
fCallBack && fCallBack.call()
}
// 事件派发
async handleQueue() {
let data = this.getQueue() // 获取第一条消息
if (this.messageQueue.length) {
if (this._messageLock) {
this._messageLock = false //消息队列上锁(排队处理)
data = this.getQueueHeadMessage() //获取头部第一条消息
this.handleMessage(data) //事件处理派发
} else {
let nowTime = new Date().getTime()
if (nowTime - data._queueRecvTime.getTime() > 10000) {
//队列存在超过10s没处理的,强制处理
this._messageLock = true
}
}
}
}
// 消息处理
handleMessage(_data) {
switch (_data.dataType) {
case 2: //订阅消息
break
case 3: //版本号广播
break
case 4: //顶号
break
case 5: //同步退出登陆
break
case 6: //行情
break
}
this.dealQueue() //时间处理完毕,继续解锁队列
}
// 延时
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// 加入消息队列
pushQueue(evtData) {
evtData._queueRecvTime = new Date()
this.messageQueue.push(evtData)
}
//获取队列头第一条消息
getQueueHeadMessage() {
if (this.messageQueue.length > 0) {
return this.messageQueue.shift()
} else {
return null
}
}
//队列消息解锁
dealQueue() {
this._messageLock = true
}
handleReconnect() {
// 如果已经达到了最大重连次数,停止重连
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('达到最大重连次数,停止重连')
return
}
if (this.isReconnecting) {
console.log('主动断开,停止重连')
return
}
console.log(`等待 ${this.reconnectInterval / 1000} 秒后重连...`)
this.reconnectAttempts += 1
this.reconnectTimeout = setTimeout(() => {
this.connect()
}, this.reconnectInterval)
}
close() {
if (this.eventSource) {
this.eventSource.close()
console.log('手动关闭连接')
}
this.reconnectTimeout && clearTimeout(this.reconnectTimeout) // 清除重连定时器
this._handleQueueID && clearInterval(this._handleQueueID) // 清除事件分发定时器
// 停止重连
this.isReconnecting = true
this.reconnectAttempts = 0 // 重置重连尝试次数
}
}
// 使用示例
const customHeaders = {
Authorization: 'Bearer your-token-here',
'Content-Type': 'application/json'
}
const requestBody = JSON.stringify({
param1: 'value1',
param2: 'value2'
})
// 统一处理回调
const handleMessage = (eventType, event) => {
switch (eventType) {
case 'onopen':
console.log('连接已打开', event)
break
case 'onmessage':
console.log('收到消息:', event.data)
break
case 'onerror':
console.error('连接错误:', event)
break
case 'onclose':
console.log('连接关闭')
break
default:
console.log(`未知事件: ${eventType}`)
}
}
const eventSource = new FetchEventSourceController('https://your-server.com/stream', {
headers: customHeaders,
body: requestBody,
reconnectInterval: 3000, // 自定义重连间隔为 3 秒
maxReconnectAttempts: 5 // 最大重连次数为 5
// handleMessage // 也可以传入统一的事件回调函数
})
// 如果需要关闭连接,可以调用:
setTimeout(() => {
eventSource.close() // 关闭连接并停止重连
}, 15000) // 15 秒后主动关闭连接
对 SSE 的简单封装
最新推荐文章于 2025-06-05 10:30:17 发布
3774

被折叠的 条评论
为什么被折叠?



