让 WebSocket 通讯像 Http 一样简单

前些天因为一个需求，必须要改用 WebSocket 进行通讯（为了绕开浏览器跨域限制），但是前面使用的接口都是用 Http 的。

心里正想着可能要大改动了……

哎嘿，怎么着，我看着之前写好的一个个请求调用方法：传参，await 等待，返回数据。

WebSocket 不也是得这样吗？发送数据，返回数据。只要把 WebSocket 的 send 方法套一个函数，然后 Promise 化，就可以像 Http 一样使用了。

实现

实现也很容易，这里使用了 @vueuse/core 的 useWebSocket 进行封装。

Copy
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)
}

这里其实就是把 Promise 的 resolve 和 reject 回调存起来了，等到收到信息后再取出来调用。

（完）