icxiaoyanz

icxiaoyanz

x

Make WebSocket communication as simple as Http

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)

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.