feat: 重新实现SseClient

This commit is contained in:
2026-06-24 09:22:14 +08:00
parent 9e36c91e13
commit 112ab3312d

View File

@@ -59,65 +59,69 @@ export class SseClient {
this._connecting = true this._connecting = true
this.abortController = new AbortController() this.abortController = new AbortController()
await fetchEventSource(this.url, { try {
headers: { saToken: token }, await fetchEventSource(this.url, {
signal: this.abortController.signal, headers: { saToken: token },
openWhenHidden: true, signal: this.abortController.signal,
openWhenHidden: true,
onopen: async (response) => { onopen: async (response) => {
const contentType = response.headers.get('content-type') || '' const contentType = response.headers.get('content-type') || ''
if (response.status === 401) { if (response.status === 401) {
throw new SseHttpError('SSE 鉴权失败', response) throw new SseHttpError('SSE 鉴权失败', response)
}
if (!response.ok) {
throw new SseHttpError(`SSE 连接失败: ${response.status}`, response)
}
if (!contentType.includes('text/event-stream')) {
throw new SseHttpError(`非法的响应类型: ${contentType || '空'}`, response)
}
this._connecting = false
this._connected = true
this.retryCount = 0
this.callbacks.onConnected?.()
},
onmessage: (message) => {
if (!message.event || !message.data) {
return
}
this.callbacks.onMessage(message.event, message.data)
},
onclose: () => {
this._connecting = false
this._connected = false
},
onerror: (error) => {
this._connecting = false
this._connected = false
// 401 → 通知外部刷新 token
if (isSseHttpError(error) && error.response.status === 401) {
this.callbacks.onUnauthorized?.()
throw error
}
this.retryCount += 1
if (this.retryCount >= this.maxRetries) {
this.callbacks.onError?.('实时连接异常,请刷新页面')
throw error
}
const delay = Math.min(
this.baseRetryDelayMs * Math.pow(2, this.retryCount - 1),
this.maxRetryDelayMs
)
return delay
} }
if (!response.ok) { })
throw new SseHttpError(`SSE 连接失败: ${response.status}`, response) } catch (error) {
} // fetchEventSource 仅在重连耗尽或 401 时 throw状态已在 onerror 中处理
if (!contentType.includes('text/event-stream')) { }
throw new SseHttpError(`非法的响应类型: ${contentType || '空'}`, response)
}
this._connecting = false
this._connected = true
this.retryCount = 0
this.callbacks.onConnected?.()
},
onmessage: (message) => {
if (!message.event || !message.data) {
return
}
this.callbacks.onMessage(message.event, message.data)
},
onclose: () => {
this._connecting = false
this._connected = false
},
onerror: (error) => {
this._connecting = false
this._connected = false
// 401 → 通知外部刷新 token
if (isSseHttpError(error) && error.response.status === 401) {
this.callbacks.onUnauthorized?.()
throw error
}
this.retryCount += 1
if (this.retryCount >= this.maxRetries) {
this.callbacks.onError?.('实时连接异常,请刷新页面')
throw error
}
const delay = Math.min(
this.baseRetryDelayMs * Math.pow(2, this.retryCount - 1),
this.maxRetryDelayMs
)
return delay
}
})
} }
/** 断开连接(主动关闭,不重连) */ /** 断开连接(主动关闭,不重连) */