From 112ab3312d727539930933780573c10f717861a3 Mon Sep 17 00:00:00 2001 From: Azure <983547216@qq.com> Date: Wed, 24 Jun 2026 09:22:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0Sse?= =?UTF-8?q?Client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/sse/SseClient.ts | 116 +++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/src/common/sse/SseClient.ts b/src/common/sse/SseClient.ts index de0d811..6ed28a0 100644 --- a/src/common/sse/SseClient.ts +++ b/src/common/sse/SseClient.ts @@ -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 中处理 + } } /** 断开连接(主动关闭,不重连) */