当前位置: 首页 > news >正文

erp网站建设百度网盘资源分享

erp网站建设,百度网盘资源分享,做MAD生肉网站,自建网站管理你这段代码实现了一个对浏览器原生 EventSource 的封装#xff0c;主要目的是更方便地管理服务端事件推送#xff08;Server-Sent Events#xff0c;简称 SSE#xff09;#xff0c;如自动构建 URL、配置管理、连接控制等。 EventSourceAPI ✅ 面试时介绍建议#xff08…你这段代码实现了一个对浏览器原生 EventSource 的封装主要目的是更方便地管理服务端事件推送Server-Sent Events简称 SSE如自动构建 URL、配置管理、连接控制等。 EventSourceAPI ✅ 面试时介绍建议逻辑清晰 面试官您好我封装了一个 EventSourceWrapper 类用于统一管理 SSEServer-Sent Events连接。这是一个单例模式实现确保在整个应用生命周期中只有一个 SSE 实例。以下是它的几个核心设计点 封装了一个 EventSourceWrapper 类用于统一管理客户端与服务端之间的 SSEServer-Sent Events连接。这个类采用单例模式确保全局只存在一个长连接实例避免重复连接的问题。它支持配置参数的动态更新通过构造函数进行依赖注入自动构建带查询参数的连接 URL并对 onopen、onmessage、onerror 和关闭事件做了统一的封装处理。考虑到原生 SSE 的自动重连机制较为有限不能监听重连次数、不知道是否已经掉线 、不可自定义重连逻辑等我还设计了一个可选的自定义重连机制支持最大重试次数、指数退避等策略使连接管理更加可控和健壮。此外它还提供了 reconnect 接口便于业务层在需要时手动触发重连。 1. 单例模式保证全局唯一性 类中通过静态方法 getESWrapperInstance 创建或复用唯一实例。适用于聊天类应用中长连接唯一的场景避免资源浪费或重复连接。 static getESWrapperInstance(): EventSourceWrapper2. 配置灵活、支持动态更新 初始配置通过构造函数传入(依赖注入内部通过 setConfig 实现对原配置的合并更新特别是 queryParams 的深层合并。 setConfig(config: PartialEventSourceConfig)3. 自动构建连接 URL 封装了 buildUrl 方法根据 queryParams 自动生成合法 SSE 请求地址。 4. 事件监听器封装清晰 onopen, onmessage, onerror, close 全部通过统一接口回调管理。错误处理逻辑健壮连接异常自动关闭并调用 onError 通知业务层。提供 reconnect 方法便于手动重连。 5. 易于拓展 可以按需添加 onRetry, onReconnect 等回调拓展性强。 //utils/EventSourceWrapper.ts import {EventSourceConfig} from ../type/EventSourceConfig;export class EventSourceWrapper {private eventSource: EventSource | null null;private config: EventSourceConfig;private isActive: boolean false;private static instance:EventSourceWrapper|nullnull;private retryCount 0;private reconnectTimer: ReturnTypetypeof setTimeout | null null;constructor(options: EventSourceConfig) {this.config {withCredentials: false,...options};}static getESWrapperInstance():EventSourceWrapper{if(!this.instance) {this.instance new EventSourceWrapper({url: /dev-api/chatStream,queryParams: {content: ,contentType: ,},onError: (err) {console.log(Error occurred:, err);}});}return this.instance;}getConfig():EventSourceConfig{return this.config;}//Partial 是 TypeScript 中的一个工具类型用于创建一个新类型其中所有属性都是可选的。setConfig(config: PartialEventSourceConfig): void {this.config {...this.config, // 保留原有配置...config, // 覆盖新配置queryParams: { // 针对嵌套对象特殊处理...this.config?.queryParams,...config?.queryParams},};}private scheduleReconnect(): void {const {autoReconnect false,maxRetries 5,retryInterval 2000,backoffMultiplier 2} this.config;if (!autoReconnect) return;if (this.retryCount maxRetries) {console.warn(SSE max retries reached);this.config.onError?.(new Error(Max reconnect attempts reached));return;}const delay retryInterval * Math.pow(backoffMultiplier, this.retryCount);console.log(Retry #${this.retryCount 1} in ${delay}ms);this.reconnectTimer setTimeout(() {this.retryCount;this.connect();}, delay);}private buildUrl(): string {const { url, queryParams } this.config;return queryParams? ${url}?${new URLSearchParams(queryParams)}: url;}connect(): void {if (this.isActive) return;try {this.isActive true;const url this.buildUrl();this.eventSource new EventSource(url, {withCredentials: this.config.withCredentials});this.eventSource.onopen (event) {console.log(SSE connection established, event);};this.eventSource.onmessage ({ data }) {this.config.onMessage?.(data);this.retryCount 0;if (this.reconnectTimer) {clearTimeout(this.reconnectTimer);this.reconnectTimer null;}};//解答完一次为什么都会报错this.eventSource.onerror (event) {console.error(SSE connection error:, event);this.close();this.config.onError?.(new Error(Connection failed));this.scheduleReconnect(); // 自定义重连};this.eventSource.addEventListener(close, () {this.close();// console.log(close)this.config.onEnd?.(SSE connection close);});} catch (error) {this.close();this.config.onError?.(error instanceof Error ? error : new Error(Connection error));}}close(): void {if (this.eventSource) {this.eventSource.close();this.eventSource null;this.isActive false;}if (this.reconnectTimer) {clearTimeout(this.reconnectTimer);this.reconnectTimer null;}}// 手动重新连接如果需要reconnect(): void {this.close();this.connect();} } ✅ Mermaid 类图 #mermaid-svg-KsJTujDuBQ5NTBWE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KsJTujDuBQ5NTBWE .error-icon{fill:#552222;}#mermaid-svg-KsJTujDuBQ5NTBWE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KsJTujDuBQ5NTBWE .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-KsJTujDuBQ5NTBWE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KsJTujDuBQ5NTBWE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KsJTujDuBQ5NTBWE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KsJTujDuBQ5NTBWE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KsJTujDuBQ5NTBWE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KsJTujDuBQ5NTBWE .marker.cross{stroke:#333333;}#mermaid-svg-KsJTujDuBQ5NTBWE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KsJTujDuBQ5NTBWE g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-KsJTujDuBQ5NTBWE g.classGroup text .title{font-weight:bolder;}#mermaid-svg-KsJTujDuBQ5NTBWE .nodeLabel,#mermaid-svg-KsJTujDuBQ5NTBWE .edgeLabel{color:#131300;}#mermaid-svg-KsJTujDuBQ5NTBWE .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-KsJTujDuBQ5NTBWE .label text{fill:#131300;}#mermaid-svg-KsJTujDuBQ5NTBWE .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-KsJTujDuBQ5NTBWE .classTitle{font-weight:bolder;}#mermaid-svg-KsJTujDuBQ5NTBWE .node rect,#mermaid-svg-KsJTujDuBQ5NTBWE .node circle,#mermaid-svg-KsJTujDuBQ5NTBWE .node ellipse,#mermaid-svg-KsJTujDuBQ5NTBWE .node polygon,#mermaid-svg-KsJTujDuBQ5NTBWE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KsJTujDuBQ5NTBWE .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-KsJTujDuBQ5NTBWE g.clickable{cursor:pointer;}#mermaid-svg-KsJTujDuBQ5NTBWE g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-KsJTujDuBQ5NTBWE g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-KsJTujDuBQ5NTBWE .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-KsJTujDuBQ5NTBWE .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-KsJTujDuBQ5NTBWE .dashed-line{stroke-dasharray:3;}#mermaid-svg-KsJTujDuBQ5NTBWE #compositionStart,#mermaid-svg-KsJTujDuBQ5NTBWE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #compositionEnd,#mermaid-svg-KsJTujDuBQ5NTBWE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #dependencyStart,#mermaid-svg-KsJTujDuBQ5NTBWE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #dependencyStart,#mermaid-svg-KsJTujDuBQ5NTBWE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #extensionStart,#mermaid-svg-KsJTujDuBQ5NTBWE .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #extensionEnd,#mermaid-svg-KsJTujDuBQ5NTBWE .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #aggregationStart,#mermaid-svg-KsJTujDuBQ5NTBWE .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE #aggregationEnd,#mermaid-svg-KsJTujDuBQ5NTBWE .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-KsJTujDuBQ5NTBWE .edgeTerminals{font-size:11px;}#mermaid-svg-KsJTujDuBQ5NTBWE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} EventSourceWrapper -eventSource: EventSource -config: EventSourceConfig -isActive: boolean -retryCount: number -reconnectTimer: Timeout -static instance: EventSourceWrapper constructor(config: EventSourceConfig) connect() close() reconnect() getConfig() setConfig(config: Partial) static getESWrapperInstance() -buildUrl() -scheduleReconnect() EventSourceConfig url: string queryParams?: Recordstring, string withCredentials?: boolean autoReconnect?: boolean maxRetries?: number retryInterval?: number backoffMultiplier?: number onMessage?:(data) onError?:(error) onEnd?:(msg: string) ✅ 面试官可能问的问题及回应建议 面试官提问应答建议为什么使用单例保证全局只有一个连接实例避免多个 SSE 并发浪费资源或产生冲突。错误处理怎么做的onerror 中做了关闭连接和触发回调并可通过 reconnect() 手动重连。如何支持动态传参通过 setConfig 合并旧配置支持 queryParams 动态更新。线程安全问题由于前端 JS 是单线程的不涉及并发修改设计上已满足需求。 MessageServiceClass 你这段代码是一个非常清晰、职责明确的封装负责处理“用户发送消息 - 通过 SSE 获取 AI 响应 - 流式展示结果”的完整流程。 // src/services/MessageService.ts import { v4 as uuidv4 } from uuid; import {ContentType} from ../type/Info.ts; import {EventSourceWrapper} from ./EventSourceWrapper.ts; import {ChatMessage} from ../type/EventSourceConfig.ts;type MessageHandler {setHistoryContent: (updater: (prev: ChatMessage[]) ChatMessage[]) void;setContent: (content: string) void;setStreamId: (id: string | null) void;esInstance: EventSourceWrapper; // SSE连接配置类型需根据实际SDK定义 };export class MessageService {private botMessageId: string | null null;constructor(private handler: MessageHandler) {}public send async (content: string) {if (!this.validateInput(content)) return;this.addUserMessage(content);this.prepareBotResponse();try {await this.establishSSEConnection(content);} catch (error) {this.handleError(error as Error);} finally {this.cleanup();}};private validateInput (content: string): boolean {const trimmed content.trim();if (!trimmed) {console.warn(空消息被拦截);return false;}return true;};private addUserMessage (content: string) {this.handler.setHistoryContent(prev [...prev,{ id: uuidv4(), author: User, text: content }]);};private prepareBotResponse () {this.botMessageId uuidv4();this.handler.setHistoryContent(prev [...prev,{id: this.botMessageId,author: Assistance,text: AI正在思考中...,isStreaming: true}]);this.handler.setStreamId(this.botMessageId);};private establishSSEConnection (content: string) {return new Promisevoid((resolve, reject) {try {this.handler.esInstance.setConfig({onMessage: (chunk: string) {this.handleChunk(chunk);}});this.handler.esInstance.setConfig({queryParams:{content,contentType: ContentType.TXT}});this.handler.esInstance.setConfig({onEnd: (arg) {console.log(arg)resolve();}})this.handler.esInstance.connect();this.handler.setContent();} catch (error) {reject(error);}});};private handleChunk (chunk: string) {if (!this.botMessageId) return;this.handler.setHistoryContent(prev prev.map(msg msg.id this.botMessageId? {...msg,text: msg.text AI正在思考中...? chunk: msg.text chunk.replace(/\\n/g, \n)}: msg));};private handleError (error: Error) {console.error(消息服务错误:, error);if (!this.botMessageId) return;this.handler.setHistoryContent(prev prev.map(msg msg.id this.botMessageId? { ...msg, text: 请求失败请重试, isStreaming: false }: msg));};private cleanup () {this.handler.setStreamId(null);this.handler.setHistoryContent(prev prev.map(msg msg.id this.botMessageId? { ...msg, isStreaming: false }: msg));this.botMessageId null;}; }✅ 逻辑梳理 整个 MessageService 类的工作流程如下 1. 用户输入消息后触发 send(content) validateInput(content)过滤掉空消息。 addUserMessage(content)将用户消息添加到历史记录。 prepareBotResponse()生成一个唯一 bot 消息 ID先展示 AI正在思考中... 的占位消息并标记为 isStreaming: true。 establishSSEConnection(content)启动 SSE 连接 设置 onMessage 处理流式片段chunk设置 onEnd 处理结束回调设置 queryParams 作为请求参数调用 connect() 启动事件源 handleChunk(chunk)每次服务端发送数据片段就更新 AI 响应内容。 若出错handleError() 会将 bot 消息内容替换为“请求失败请重试”。 最后调用 cleanup()关闭流式标记、重置状态。 一句话向面试官介绍这个类 我封装了一个 MessageService 类专门用于管理用户消息的发送与 AI 响应的流式展示。它通过注入的 handler 操作 UI 层状态并结合我封装的 EventSourceWrapper 实现稳定的 SSE 长连接支持自动重连、状态清理与错误处理等能力。整个消息流转过程被拆分为输入校验、消息插入、连接建立、数据处理、异常兜底、连接回收六个步骤逻辑清晰、职责单一有利于维护和扩展。 Mermaid 类图 #mermaid-svg-X5Zpyqsc8eqqma8k {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-X5Zpyqsc8eqqma8k .error-icon{fill:#552222;}#mermaid-svg-X5Zpyqsc8eqqma8k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X5Zpyqsc8eqqma8k .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-X5Zpyqsc8eqqma8k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X5Zpyqsc8eqqma8k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X5Zpyqsc8eqqma8k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X5Zpyqsc8eqqma8k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X5Zpyqsc8eqqma8k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X5Zpyqsc8eqqma8k .marker.cross{stroke:#333333;}#mermaid-svg-X5Zpyqsc8eqqma8k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X5Zpyqsc8eqqma8k g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-X5Zpyqsc8eqqma8k g.classGroup text .title{font-weight:bolder;}#mermaid-svg-X5Zpyqsc8eqqma8k .nodeLabel,#mermaid-svg-X5Zpyqsc8eqqma8k .edgeLabel{color:#131300;}#mermaid-svg-X5Zpyqsc8eqqma8k .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-X5Zpyqsc8eqqma8k .label text{fill:#131300;}#mermaid-svg-X5Zpyqsc8eqqma8k .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-X5Zpyqsc8eqqma8k .classTitle{font-weight:bolder;}#mermaid-svg-X5Zpyqsc8eqqma8k .node rect,#mermaid-svg-X5Zpyqsc8eqqma8k .node circle,#mermaid-svg-X5Zpyqsc8eqqma8k .node ellipse,#mermaid-svg-X5Zpyqsc8eqqma8k .node polygon,#mermaid-svg-X5Zpyqsc8eqqma8k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-X5Zpyqsc8eqqma8k .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-X5Zpyqsc8eqqma8k g.clickable{cursor:pointer;}#mermaid-svg-X5Zpyqsc8eqqma8k g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-X5Zpyqsc8eqqma8k g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-X5Zpyqsc8eqqma8k .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-X5Zpyqsc8eqqma8k .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-X5Zpyqsc8eqqma8k .dashed-line{stroke-dasharray:3;}#mermaid-svg-X5Zpyqsc8eqqma8k #compositionStart,#mermaid-svg-X5Zpyqsc8eqqma8k .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #compositionEnd,#mermaid-svg-X5Zpyqsc8eqqma8k .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #dependencyStart,#mermaid-svg-X5Zpyqsc8eqqma8k .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #dependencyStart,#mermaid-svg-X5Zpyqsc8eqqma8k .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #extensionStart,#mermaid-svg-X5Zpyqsc8eqqma8k .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #extensionEnd,#mermaid-svg-X5Zpyqsc8eqqma8k .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #aggregationStart,#mermaid-svg-X5Zpyqsc8eqqma8k .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k #aggregationEnd,#mermaid-svg-X5Zpyqsc8eqqma8k .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-X5Zpyqsc8eqqma8k .edgeTerminals{font-size:11px;}#mermaid-svg-X5Zpyqsc8eqqma8k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MessageService -botMessageId: string -handler: MessageHandler constructor(handler: MessageHandler) send(content: string) -validateInput(content: string) -addUserMessage(content: string) -prepareBotResponse() -establishSSEConnection(content: string) -handleChunk(chunk: string) -handleError(error: Error) -cleanup() EventSourceWrapper ChatMessage id: string author: string text: string isStreaming?: boolean MessageHandler esInstance: EventSourceWrapper setHistoryContent(updater) setContent(content) setStreamId(id) ✅ 亮点总结面试加分点 亮点说明状态分离MessageService 不直接操作 UI而是通过 MessageHandler 依赖注入解耦逻辑与视图。渐进式流处理支持服务端分片返回chunk消息实时拼接展示。可拓展性强错误处理、连接状态、Bot 消息标记都独立封装便于扩展如中断续传、撤回、重试等功能。复用性好配合封装的 EventSourceWrapper可以在多个模块中复用消息推送逻辑。
http://www.hkea.cn/news/14490823/

相关文章:

  • 公众号里的网站怎么做滇中引水建设管理局网站
  • 网站建设前的ER图电子商务网站怎么做seo
  • 上海网站建设上海门户网站营销策略
  • 广州建设官方网站网站建设在马来西亚
  • html 如何嵌入网站页面软件设计思路
  • 福建省住房城乡和建设厅网站重庆给商家企业做网站
  • 石油化工工程建设人才招聘网站2023前端就业形势
  • 网站建设评估体系住房建设部官方网站设计费计取
  • 大兴专业网站建设公司公司如何进行网络推广
  • 2017做哪些网站致富怎么网上接网站开发单自己做
  • 福建建设执业管理中心网站网站建设襄阳
  • 秒收录网站深圳网站设计合理刻
  • 域名对网站排名的影响个人网站介绍模板
  • 网站网页设计0基础学保定seo关键词优化外包
  • 中卫网站建设多少钱安卓手机做网站
  • 外贸网站搭建难不难网站设计目标 优帮云
  • 求个网站谢谢啦网站后台jsp怎么做分页
  • 企业网站快速排名厦门购买域名以后搭建网站
  • 湖南城乡住房建设厅网站苏州响应式网站建设
  • 诚信通开了网站谁给做深圳出台科技支持政策
  • 网站搜索引擎优化方案百度竞价托管哪家好
  • 网站建设的基本元素备案做电影网站吗
  • 织梦文章类网站模板网站定制开发注意事项
  • 商城网站的psd模板免费下载2网站建设总结
  • 工信部网站备案进度查询眉山网站建设哪家好
  • 鞍山网站建设公司wordpress 相册模板
  • 江苏中盛建设集团网站网站建设与管理教学视频下载
  • 常州好的网站设计公司崇左网站建设公司
  • node 做的大型网站网站后台怎么上传网页模板
  • 海沧做网站企业型网站建设制作平台