feat: 重新实现SseClient
This commit is contained in:
@@ -59,65 +59,69 @@ export class SseClient {
|
||||
this._connecting = true
|
||||
this.abortController = new AbortController()
|
||||
|
||||
await fetchEventSource(this.url, {
|
||||
headers: { saToken: token },
|
||||
signal: this.abortController.signal,
|
||||
openWhenHidden: true,
|
||||
try {
|
||||
await fetchEventSource(this.url, {
|
||||
headers: { saToken: token },
|
||||
signal: this.abortController.signal,
|
||||
openWhenHidden: true,
|
||||
|
||||
onopen: async (response) => {
|
||||
const contentType = response.headers.get('content-type') || ''
|
||||
onopen: async (response) => {
|
||||
const contentType = response.headers.get('content-type') || ''
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new SseHttpError('SSE 鉴权失败', response)
|
||||
if (response.status === 401) {
|
||||
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)
|
||||
}
|
||||
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
|
||||
}
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
// fetchEventSource 仅在重连耗尽或 401 时 throw,状态已在 onerror 中处理
|
||||
}
|
||||
}
|
||||
|
||||
/** 断开连接(主动关闭,不重连) */
|
||||
|
||||
Reference in New Issue
Block a user