简体   繁体   English

SSE - 服务器发送事件 - 如何重用相同的 EventSource

[英]SSE - Server Sent Events - How to reuse same EventSource

I'm new in programming and trying to understand SSE.我是编程新手并试图了解 SSE。 I'm using React for frontend, Node.js for backend and MongoDB for database.我将 React 用于前端,Node.js 用于后端,MongoDB 用于数据库。 Sorry for my english in advance.对不起我的英语提前。

When i open the website, creating eventsource and start to listen backend "/test".当我打开网站时,创建事件源并开始监听后端“/test”。 I have a button in frontend and when i click it, a random float number between 0 and 1 posted backend "/savedata".我在前端有一个按钮,当我点击它时,一个介于 0 和 1 之间的随机浮点数发布了后端“/ savedata”。 Also showing numbers bigger than 0.5.还显示大于 0.5 的数字。

In server side for "/test", checking database for the new records every 3 seconds and if recorded numbers is bigger than 0.5, send it to frontend and delete the database record.在服务器端“/test”,每3秒检查一次数据库是否有新记录,如果记录的数字大于0.5,则将其发送到前端并删除数据库记录。 For "/savedata", saving numbers to database coming from frontend.对于“/savedata”,将数字保存到来自前端的数据库。

My Question is;我的问题是;

when i open website in a new tab, another eventsource is being created and trigger database "/test".当我在新选项卡中打开网站时,正在创建另一个事件源并触发数据库“/test”。 Is there a way to reuse eventsource instead of create a new eventsource?有没有办法重用事件源而不是创建新的事件源? Also if you have any suggestions for my code, pls tell me.另外,如果您对我的代码有任何建议,请告诉我。 i'm trying to learn.我正在尝试学习。

Here is my code;这是我的代码;

Frontend React - FrontendTest.js前端反应 - FrontendTest.js

import axios from 'axios'
import React, { useEffect, useState } from 'react'


const FrontendTest = () => {
 const [data, setData] = useState(null)
 const [databaseInfo, setDatabaseInfo] = useState(null)


 let number = 0
 const url = 'http://localhost:5000/test'
 let source

 useEffect(() => {
    source = new EventSource(url)
    source.onmessage = (e) => {
    setData(JSON.parse(e.data))
    }
  }, [])

 const buttonClicked = async (e) => {
    e.preventDefault()
    number = Math.random()
    const sendReq = await 
 axios.post('http://localhost:5000/savedata', {
      number,
    })
    setDatabaseInfo(sendReq.data)
  }

  return (
    <div>
      <div>
        <button onClick={buttonClicked}>Send</button>
        <p>{`If the number > 0.5 , it will be founded`}</p>
        <p>
          {databaseInfo &&
            `${databaseInfo.data.toFixed(4)} Saved to 
 Database !`}
        </p>
        <p>
          {data && `${data.toFixed(4)} Founded ! Database 
 input deleted !   `}
        </p>
      </div>
     </div>
  )
}

Node.js - server.js Node.js-server.js

import express from 'express'
import cors from 'cors'
import expressAsyncHandler from 'express-async-handler'
import mongoose from 'mongoose'
import Datas from './model.js'


const app = express()
const port = 5000

app.use(
  cors({
    origin: 'http://localhost:3000',
    credentials: true,
  })
)
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

let interval

app.post(
  '/savedata',
  expressAsyncHandler(async (req, res) => {
    const data = req.body.number
    const result = await Datas.create({
      data1: data,
    })
    res.send({ data })
  })
)

app.get(
  '/test',
  expressAsyncHandler(async (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream')
    res.setHeader('Cache-Control', 'no-cache')
    clearInterval(interval)
    interval = setInterval(async () => {
      const database = await Datas.find({})
      const databaseData1 = database.map((item) => item.data1)
      const databaseIds = database.map((item) => item._id)
      const data = {
        value: databaseData1,
      }

      for (let i = 0; i < data.value.length; i++) {
        if (data.value[i] > 0.5) {
          console.log(data.value[i])
          res.write(`data: ${JSON.stringify(data.value[i])}\n\n`)
          await Datas.findByIdAndDelete(databaseIds[i])
        }
      }
      console.log('Searching')
    }, 3000)
  })
)

mongoose
  .connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() =>
    app.listen(port, () =>
      console.log(`Example app listening at
    http://localhost:${port}`)
    )
  )
  .catch((error) => console.log(error))

You can't directly share the event source handle, but what you are able to do is share data between tabs, when they are in the same origin.您不能直接共享事件源句柄,但您可以做的是在选项卡之间共享数据,当它们位于同一来源时。

One approach would be LocalStorage.一种方法是 LocalStorage。 So the first tab would write to the storage that it is going to run the EventSource connection.因此,第一个选项卡将写入将运行 EventSource 连接的存储。 The second tab that connects would see there is already another tab open, and start listening.连接的第二个选项卡会看到已经打开了另一个选项卡,然后开始收听。 Then each time an event comes into the first tab, it writes it to local storage, and the second tab can see it.然后每次事件进入第一个选项卡时,它都会将其写入本地存储,第二个选项卡可以看到它。

(You need to handle the case of what happens if the first tab closes; especially if there are multiple other tabs listening in, so be aware that making this production-ready is going to get quite complicated.) (您需要处理如果第一个选项卡关闭时会发生什么情况;尤其是如果有多个其他选项卡在监听,因此请注意,使这个生产就绪会变得相当复杂。)

Another WebAPI that is specifically for doing that kind of thing is Broadcast Channel .另一个专门用于做这种事情的 WebAPI 是Broadcast Channel I've not used it: the browser support is almost the same as EventSource, but Safari is lagging behind.我没用过:浏览器支持和EventSource差不多,但是Safari落后了。

It looks like this question was created to keep track of the various approaches.看起来这个问题是为了跟踪各种方法而创建的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM