前端在处理后端通过webSocket推送消息的时候,容易出现后端同时发送两条不同type的情况(如:第一条消息发送过来的时间是2024/03/15 09:22:01.360;第二条消息发送过来的时间2024/03/15 09:22:01.361),如果用redux统一处理类型并分发对应的reducer,就会出现同时调用一个action,并且第二条消息把第一条消息覆盖的情况。
针对这一问题,前端可以使用队列来解决,声明一个空数组,来一条消息就往数组里去push,再写一个定时器,每隔100毫秒取数组最后一条消息去触发redux的action(先进先出),就不会出现同时触发的情况了。代码示例:
// 自定义hooks
const useWebsocket = ({ url }) => {
const ws = useRef<any>(null)
const [wsData, setMessage] = useState('')
const [readyState, setReadyState] = useState({ key: 0, value: '正在链接中' })
const token = getToken()
const cacheWsArr = useRef<any[]>([])
const handleMessage = () => {
setInterval(() => {
if (cacheWsArr.current.length > 0) {
// 每隔一百毫秒从数组最后一项取
const data = cacheWsArr.current.shift()
setMessage(JSON.stringify(data))
}
}, 100)
}
const creatWebSocket = () => {
const stateArr = [{ key: 0, value: '正在链接中' }, { key: 1, value: '已经链接并且可以通讯' }, { key: 2, value: '连接正在关闭' }, { key: 3, value: '连接已关闭或者没有链接成功' }]
try {
getWsUrl().then(res => {
if (res && res.code !== 200) return
ws.current = new WebSocket(`${res.data}${url}`, [token])
ws.current.onopen = (_e) => {
setReadyState(stateArr[ws.current?.readyState ?? 0])
}
ws.current.onclose = (e) => {
setReadyState(stateArr[ws.current?.readyState ?? 0])
}
ws.current.onerror = (e) => {
setReadyState(stateArr[ws.current?.readyState ?? 0])
}
ws.current.onmessage = (e) => {
const obj = JSON.parse(e.data)
const data = {
...obj,
type: obj.type + `_${new Date().getTime()}`,
}
// 往缓存消息的数组中添加!!
cacheWsArr.current.push(data)
}
})
} catch (error) {
console.log(error)
}
}
const webSocketInit = () => {
if (!ws.current || ws.current.readyState === 3) {
creatWebSocket()
// 初始化时创建定时器
handleMessage()
}
}
// 关闭 WebSocket
const closeWebSocket = () => {
ws.current?.close()
}
const reconnect = () => {
try {
closeWebSocket()
ws.current = null
creatWebSocket()
} catch (e) {
console.log(e)
}
}
useEffect(() => {
if (token) {
webSocketInit()
}
return () => {
ws.current?.close()
}
}, [ws, token])
return {
wsData,
readyState,
closeWebSocket,
reconnect
}
}
export default useWebsocket
欢迎大佬们提出优化意见~