记录一次EventSourse请求(falsk+react)

记录一次EventSourse请求(falsk+react)

背景

需求需要后端有新的数据时传给前端,因为新的数据不确定什么适合会来,用轮询有点浪费.后来考虑用websocket,wb用起来太复杂,便发现了前端接口eventsource,建立连接后后端单方面给前端传数据就行,简单好用.

后端是用flask写的,前端用的是react

实现

前端建立连接并接收数据

typescript 复制代码
// 建立连接
const eventSource = new EventSource("/es");
// 连接成功
eventSource.onopen = function () {
  console.log("open")
}
// 接受消息
eventSource.onmessage = function (ev) {
  console.log("event")
  console.log(ev.data)
};
// 发生错误
eventSource.onerror = function () {
  console.log("error");
  eventSource.close();
};

后端响应数据

后端实现很简单,将响应的数据类型设置为text/event-stream就可以,然后多次响应,就可以多次像前端传数据了

python 复制代码
@app.route("/es", methods=["GET"])
def es():
    def eventStream():
        while True:
            time.sleep(1)
            yield "test"

    return Response(eventStream(), mimetype="text/event-stream")

起初这样写,通过接口工具测试时没问题,可以正常接收响应,但是在浏览器里却不正常显示.搜索发现前端需要接受特定的格式内容id:1\nevent:message\ndata:test\n\n,id:可以省略,event:message是固定的,用其他名称的话需要前端自定义(这里没有深入研究),data:是固定的要传输的数据的格式,于是做了改进,这样浏览器就可以接收到数据内容了

python 复制代码
@app.route("/es", methods=["GET"])
def es():
    def eventStream():
        while True:
            time.sleep(1)
            yield "event: message\ndata:test"\n\n"

    return Response(eventStream(), mimetype="text/event-stream")

优化

测试发现,前端关闭连接后,后端还在持续响应数据.于是在前端请求数据时增加了link_id,关闭连接时调用后端提供的关闭连接接口来停止后端的响应.

前端使用useEffect(),进入时请求连接,离开时请求关闭

typescript 复制代码
// 生成uuid
const linkId = useId()

useEffect(() => {
    const eventSource = new EventSource("/es?link_id=" + linkId);
    eventSource.onopen = function () {
      console.log("open")
    }
    eventSource.onmessage = function (ev) {
      console.log("event")
		  console.log(ev.data)
    };
    eventSource.onerror = function () {
      console.log("error");
      eventSource.close();
    };

    // 关闭连接时
    return () => {
      axios.get("/es/close?link_id=" + linkId).then((res) => {
        console.log(res.data)
      })
      eventSource.close()
    }

  }, []);

后端使用一个集合来存放前端请求时的link_id,请求关闭时将link_id加入集合.检测到当前请求的link_id在集合中时便关闭连接

typescript 复制代码
# 集合用于存放link_id
closeSets = set()

# 关闭连接请求
@app.route("/es/close", methods=["GET"])
def closeEvent():
    linkId = request.args.get("link_id")
    closeSets.add(linkId)
    return "success"

# 请求连接
@app.route("/es", methods=["GET"])
def es():
    linkId = request.args.get("link_id")

    def eventStream(linkId):
        while True:
            # 关闭连接
            if linkId in closeSets:
                closeSets.remove(linkId)
                break

            time.sleep(1)
            yield "event: message\ndata:test"\n\n"

    return Response(eventStream(linkId), mimetype="text/event-stream")

参考

segmentfault.com/q/101000004...
segmentfault.com/q/101000004...

相关推荐
我叫张小白。几秒前
TypeScript对象类型与接口:构建复杂数据结构
前端·javascript·typescript
墨客希7 分钟前
如何快速掌握大型Vue项目
前端·javascript·vue.js
大福ya21 分钟前
AI开源项目改造NextChat(ChatGPT-Next-Web)实现前端SSR改造打造一个初始框架
前端·chatgpt·前端框架·开源·aigc·reactjs·ai编程
databook22 分钟前
让你的动画“活”过来:Manim 节奏控制指南 (Rate Functions)
后端·python·动效
n***333533 分钟前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
XUN4J37 分钟前
深入浅出谈谈RPC框架
后端
Java水解42 分钟前
功能全面的PostgreSQL图形化管理工具pgAdmin3实战详解
后端·postgresql
纯粹的热爱1 小时前
🌐 阿里云 Linux 服务器 Let's Encrypt 免费 SSL 证书完整部署指南
前端
北辰alk1 小时前
Vue3 自定义指令深度解析:从基础到高级应用的完整指南
前端·vue.js
毕设源码-邱学长1 小时前
【开题答辩全过程】以 基于SpringBoot的医院血库管理系统设计与实现为例,包含答辩的问题和答案
java·spring boot·后端