A few days ago, due to a requirement, I had to switch to using WebSocket for communication (to bypass the browser's cross-origin restrictions), but the interfaces I had been using before were all based on Http.
I was thinking that it might require a major change...
Well, I looked at the request calling methods I had written earlier: passing parameters, await
waiting, returning data.
Isn't WebSocket the same? Sending data, receiving data. As long as I wrap the WebSocket's send
method in a function and turn it into a Promise, I can use it just like Http.
Implementation#
The implementation is also quite easy; here I used @vueuse/core
's useWebSocket
for encapsulation.
import { useWebSocket } from '@vueuse/core'
import { toast } from 'vue-sonner'
interface WebSocketHandler {
resolve: (data: any) => void
reject: (data: any) => void
}
const messageHandlers = new Map<string, WebSocketHandler>()
const ws = useWebSocket(
'ws://localhost:10000/ws',
{
autoReconnect: {
delay: 2000,
retries: 3,
},
heartbeat: {
interval: 30000,
pongTimeout: 3000,
responseMessage: 'pong',
},
onMessage: (_, event) => {
if (!event.data.startsWith('{"id"')) return
const response = JSON.parse(event.data)
const handler = messageHandlers.get(response.id)
if (!handler) return
const isSuccess = response.code === 200
if (isSuccess) handler.resolve(response.data)
else {
toast.warning(response.message)
handler.reject(new Error(response.message))
}
}
}
)
export function wsFetch<T>(params: T) {
return new Promise<T>((resolve, reject) => {
if (ws.status.value !== 'OPEN') {
reject(new Error('WebSocket is not connected'))
return
}
const id = getRequestId()
ws.send(JSON.stringify({ ...params, id }))
messageHandlers.set(id, { resolve, reject })
})
}
function getRequestId() {
return Date.now().toString() + Math.random().toString(36).slice(2, 9)
}
Here, I am actually just storing the Promise's resolve
and reject
callbacks, and calling them later when the message is received.
(End)