50 行 SSE + 打字机效果:从零跑通服务端推流 Demo 📅 发布时间:2026/7/13 6:00:01 👁️ 浏览次数: 50 行 SSE 打字机效果从零跑通服务端推流 Demo复制本文代码即可运行。效果浏览器打开页面后服务端每 5 秒推送一轮回复文字逐段流式出现类似 AI 对话打字机。你将得到什么一条SSE 长连接GET /sse-stream服务端把整句拆成delta片段推送前端收到一段、追加一段实现打字机心跳保活 客户端断开清理运行效果示意[11:21:15] 服务端定时推送第1条消息这是打字机效果演示。 [11:21:20] 服务端定时推送第2条消息这是打字机效果演示。 ...一、创建项目mkdirsse-democdsse-demonpminit-ynpminstallexpress目录结构sse-demo/ ├── package.json ├── server.js └── index.html二、package.json{name:sse-demo,version:1.0.0,scripts:{dev:node server.js},dependencies:{express:^5.2.1}}三、server.js完整可复制constexpressrequire(express);constappexpress();constport3000;// 静态托管前端页面index.htmlapp.use(express.static(./));// 模拟一句完整回复拆成逐段 delta 推送functionpushTypewriterMessage(res,num){constfullText服务端定时推送第${num}条消息这是打字机效果演示。;letindex0;consttypeTimersetInterval((){if(indexfullText.length){clearInterval(typeTimer);res.write(data:${JSON.stringify({type:done,id:num})}\n\n);return;}constchunkfullText.slice(index,index2);indexchunk.length;constdataJSON.stringify({type:delta,id:num,delta:chunk,});res.write(data:${data}\n\n);},80);returntypeTimer;}// SSE 接口app.get(/sse-stream,(req,res){// SSE 必须的响应头res.setHeader(Content-Type,text/event-stream);res.setHeader(Cache-Control,no-cache);res.setHeader(Connection,keep-alive);res.setHeader(Access-Control-Allow-Origin,*);// 心跳包防止网关/nginx 超时断开constheartBeatTimersetInterval((){res.write(data: ping\n\n);},10000);// 业务定时推送每 5 秒模拟一轮新回复letnum0;consttypeTimers[];constpushTimersetInterval((){num;consttimerpushTypewriterMessage(res,num);typeTimers.push(timer);},5000);// 客户端关闭连接清除定时器req.on(close,(){console.log(客户端关闭连接清除定时器);clearInterval(heartBeatTimer);clearInterval(pushTimer);typeTimers.forEach(clearInterval);res.end();});});app.listen(port,(){console.log(SSE服务运行在 http://localhost:${port});});四、index.html完整可复制!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8titleSSE 实时推送 Demo/titlestylebody{padding:20px;font-size:16px;}#msgList{margin-top:20px;border:1px solid #eee;padding:10px;min-height:300px;max-height:500px;overflow-y:auto;}.item{padding:6px 0;border-bottom:1px solid #f5f5f5;line-height:1.6;}.item .time{color:#666;margin-right:4px;}.ping{color:#999;font-size:12px;}/style/headbodyh3SSE 服务端定时消息实时展示打字机效果/h3dividstatus连接状态未连接/divdividmsgList/divscriptconststatusEldocument.getElementById(status);constlistEldocument.getElementById(msgList);letcurrentLinenull;constssenewEventSource(http://localhost:3000/sse-stream);sse.onopen(){statusEl.textContent连接状态已建立;statusEl.style.colorgreen;};sse.onmessage(e){consttexte.data;if(textping){appendPing();return;}constdataJSON.parse(text);if(data.typedelta){if(!currentLine||currentLine.dataset.id!String(data.id)){currentLinedocument.createElement(div);currentLine.classNameitem;currentLine.dataset.iddata.id;consttimeSpandocument.createElement(span);timeSpan.classNametime;timeSpan.textContent[${newDate().toLocaleTimeString()}];currentLine.appendChild(timeSpan);consttextSpandocument.createElement(span);textSpan.classNametext;currentLine.appendChild(textSpan);listEl.appendChild(currentLine);}currentLine.querySelector(.text).textContentdata.delta;listEl.scrollToplistEl.scrollHeight;return;}if(data.typedone){currentLinenull;}};sse.onerror(){statusEl.textContent连接状态断开/异常正在重连...;statusEl.style.colorred;};functionappendPing(){constdivdocument.createElement(div);div.classNameitem ping;div.innerText[心跳包]${newDate().toLocaleTimeString()};listEl.appendChild(div);listEl.scrollToplistEl.scrollHeight;}// 手动关闭连接// sse.close();/script/body/html五、启动npmrun dev浏览器访问http://localhost:3000若端口被占用先停掉旧进程再启动lsof-ti:3000|xargskill-9npmrun dev六、核心原理1. SSE 消息格式每条消息必须是data: {type:delta,id:1,delta:服务}末尾两个换行\n\n表示一条 event 结束。2. 自定义协议delta donetype含义示例delta增量文本片段{ type:delta, id:1, delta:服务 }done本轮回复结束{ type:done, id:1 }ping心跳纯字符串ping这和 AI 对话很像服务端不断推delta客户端text delta而不是一次推整句。3. 时序浏览器 服务端 │ │ │── GET /sse-stream ─────►│ 建立 SSE 长连接 │◄── delta: 服务 ────────│ 80ms │◄── delta: 端定 ────────│ 80ms │◄── delta: 时推 ────────│ ... │◄── done ─────────────────│ 本轮结束 │ │ 5 秒后下一轮 │◄── delta: 服务 ────────│ id2 新一轮 │◄── ping ─────────────────│ 10s 心跳4. 前端打字机逻辑// 新 id → 新建一行// 同 id → 往 .text 追加 deltacurrentLine.querySelector(.text).textContentdata.delta;// done → 释放 currentLine下一轮 delta 会开新行if(data.typedone)currentLinenull;5. 心跳为什么需要Nginx / 负载均衡对空闲长连接常有 60s 超时。每 10s 推ping避免连接被中间层静默掐断。生产环境更推荐 SSE 注释行: ping\n\n浏览器会自动忽略不进onmessage。6. 断开检测req.on(close,(){clearInterval(heartBeatTimer);clearInterval(pushTimer);typeTimers.forEach(clearInterval);res.end();});关 Tab、刷新、sse.close()都会触发避免服务端定时器泄漏。七、可调参数在server.js的pushTypewriterMessage里参数当前值效果index 2每次 2 字chunk 越大跳字感越强8080ms越小打字越快50005s两轮回复间隔1000010s心跳间隔八、和 AI 对话的对应关系Demo生产如灵犀 SSEdelta字段flowResult.outContent增量doneSSE 连接关闭 finishReason: stopEventSourceGETfetchEventSourcePOST 鉴权textContent deltamarkdownRender.addChunk(delta)这个 Demo 用原生EventSource GET适合理解原理真实 AI 接口一般是POST SSE需要microsoft/fetch-event-source等库。九、常见问题Q看不到打字效果整句一次出来多半是旧版 server 还在跑重启node server.js或在 Network 里确认收到的是{type:delta,...}而不是整句 JSON。Qonerror一直红字EventSource断线会自动重连onerror可能短暂出现一般可恢复。Q想改成 POST SSE原生EventSource不支持 POST需换fetch 流式读 body或microsoft/fetch-event-source。十、小结这个 Demo 覆盖了 SSE 流式 UI 的关键链路服务端长连接 res.write逐段推delta客户端EventSource.onmessage 增量追加工程化心跳保活、req.on(close)清理三个文件、npm run dev、打开浏览器——就能本地复现 AI 式打字机效果。
Netflix现象级剧集成功要素剖析:从叙事结构到制作水准 这次我们来看一部在网飞平台创下收视纪录的现象级剧集。这部剧不仅在全球范围内引发了观剧热潮,更以其独特的叙事风格和深刻的社会洞察成为了文化讨论的焦点。对于内容创作者、影视从业者以及关注流媒体趋势的读者来说,了解这部剧的成功要素具有重要的参… 2026/7/13 6:00:01
Hyperledger Fabric 2.5 构建成绩管理系统:3节点联盟链部署与性能实测 Hyperledger Fabric 2.5 构建成绩管理系统:3节点联盟链部署与性能实测区块链技术在教育数据管理领域的应用正从概念验证走向实际落地。去年某高校发生的成绩篡改事件直接导致37名学生留学申请受影响,暴露出传统中心化系统的脆弱性。本文将手把手带您用Hy… 2026/7/13 5:58:00
高并发内存池 - 申请内存过程联调 高并发内存池 - 申请内存过程联调 到此我们已经将内存池中 “申请内存” 功能的逻辑实现的七七八八了,接下来我们将设计一个测试函数,看看目前的功能是否能跑通。 UintTest.cpp #include "Comm.h" #include "ConcurrentAlloc.h" … 2026/7/13 5:56:00
《辐射4》Ubuntu指针MOD制作:从资源转换到MO2部署全流程 在游戏社区中,为《辐射4》这类开放世界角色扮演游戏制作和安装MOD是提升游戏体验的重要方式。有些玩家不满足于替换游戏内的武器、服装或建筑,还希望将操作系统的视觉元素也融入游戏世界,比如将默认的鼠标指针替换为Ubuntu系统的经典样式。这… 2026/7/13 7:16:39
瑞盛祥粉丝品牌知识科普:扎根中国粉丝之都招远,传承三百年龙口粉丝非遗技艺 一、品牌基本信息瑞盛祥是招远市五福食品有限公司旗下专注于粉丝品类的商标品牌。该商标于2017年3月1日提交注册申请,注册号22988387,国际分类为第30类(方便食品),专用权期限为2018年2月28日至2028年2月27日。商标核定… 2026/7/13 7:14:39
STM32L442KC与ADS8665构建高精度数据采集系统 1. 项目概述:ADS8665与STM32L442KC的强强联合在工业自动化、医疗设备和测试测量领域,高精度信号采集系统对模数转换器(ADC)的性能要求极为严苛。ADS8665作为TI推出的16位1MSPS SAR型ADC,以其卓越的线性度(0.5LSB INL)和低功耗特性(5mW1MSPS)成… 2026/7/13 7:12:39
ADS7828与PIC32MZ的嵌入式信号采集系统设计 1. 项目背景与核心器件选型在嵌入式系统开发中,模拟信号采集是连接物理世界与数字系统的关键环节。ADS7828作为德州仪器(TI)推出的12位精度ADC芯片,以其低功耗特性(工作电流仅250μA)和8通道多路复用能力,成为中精度数据采集的理想选择。搭配… 2026/7/13 7:10:38
STM32F303VE GPIO上拉下拉配置与信号完整性优化 1. 信号上拉与下拉的基础原理在嵌入式系统设计中,信号线的上拉和下拉配置是确保电路可靠工作的基础。上拉电阻将信号线拉向高电平(通常接VCC),而下拉电阻则将信号线拉向低电平(通常接GND)。这种配置在数字电… 2026/7/13 7:10:38
Spark 3.5.0 与 Hadoop 3.3.6 环境变量冲突排查:5 个关键配置项详解 Spark 3.5.0 与 Hadoop 3.3.6 环境变量冲突排查:5 个关键配置项详解当企业级大数据平台从传统Hadoop生态向Spark计算引擎迁移时,环境变量配置不当导致的兼容性问题成为最常见的"拦路虎"。本文将深入剖析Spark与Hadoop集成时最易引发冲突的5个核… 2026/7/13 7:10:38
HS2-HF Patch终极指南:如何用3步解决Honey Select 2的70+个痛点 HS2-HF Patch终极指南:如何用3步解决Honey Select 2的70个痛点 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch 你是否曾经在Honey Select 2中遇到过… 2026/7/13 0:01:19
语音转文字工具AsrTools:让音频整理变得简单高效 语音转文字工具AsrTools:让音频整理变得简单高效 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into accurate text … 2026/7/13 0:03:19
基于深度学习的蘑菇或花卉或动漫人物或中草药货水果蔬菜等识别系统31(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_ 基于深度学习的蘑菇或花卉或动漫人物或中草药货水果蔬菜等识别系统31(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_ 独家界面!不会重复,此项目属于本人原创,若有雷同,均是盗卖,各位买… 2026/7/13 0:05:20
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/13 2:34:55