实现多个websocket串行请求

921 阅读2分钟

简介

在实现编辑器debug功能时。前端与Node中间层通信都是通过websocket来实现的。我们都知道websocket的请求与响应可能不在同一时间同一地方触发. 就拿此次debug功能需求来说 先要调用initialize事件初始化成功之后再调用SetBreakpoints事件成功之后才接收stopped事件,所有的事件调用都存在先后顺序。具体debug如何实现可以仔细阅读Debug Adapter Protocol文档

实现

接下来就实现串行的websocket请求方法 (类似http中使用await等待这次请求返回再执行后续操作)
发送/请求可以在于任何地方,并且同时可以存在同一时间多个相同websocket事件的派发

第一步: 定义一个Client类并且声明requestQueue用于存储每一个ws事件的idresolve回调函数

class Client {
  // 定义一个Map数据用于存储,发送的ws回调
  requestQueue: Map<number, (data: any) => void>;
  constructor() {
    this.requestQueue = new Map();
  }
}

第二步: 在Client类中添加request方法,该方法为ws请求方法并把这个这次的promise的resolveid建立映射关系,并且存入到requestQueue中。会在后续的onMessage回调中进行消费
TIPS: promise的状态分为三种pending | rejected | rejected.
文章推荐: promise源码实现

request(id, eventName, options) {
    // 发送ws请求, 并且将此次id存储到requestQueue,只有当ws服务器有返回本次请求才算完成
    return new Promise((resolve) => {
      socket.send(eventName, {id, ...options}); // socket事件发送
      this.requestQueue.set(id, (data) => {
        resolve(data);
      });
    });
}

第三步: 在Client类中监听message事件,并且消费requestQueue中的对应数据

  constructor() {
    ...
    this.socket.addEventListener("message", (event: MessageEvent<any>) => {
      const { data } = event;
      const res = JSON.parse(data);
      const { id } = res;
      if (id !== undefined) {
        // 接收到ws服务器返回的事件id,从requestQueue执行对应的resolve相关回调操作
        const callback = this.requestQueue.get(id);
        callback?.(res);
        this.requestQueue.delete(id);
      }
    });
  }

第四步: 调用request方法编写,使用await进行等待,这样我们就能实现串行了

// 调用request请求
class test {
  private client: Client;
  constructor() {
    this.client = new Client();
  }
  async initialize() {
    // 执行request请求,request是一个ws的请求
    await this.client.request(uuid,"initialize");
    await this.client.request(uuid,"SetBreakpoints");
    // 执行后续操作...
  }
}

第五步: 执行第二步request请求时有可能服务异常,此时我们可以添加timeOut方法和结合Promise.race来实现超时操作

function timeOut(delay: number): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('请求超时');
    }, delay);
  });

// 使用
return Promise.race([request(...), _timeOut(2000)]);

总结

以上就是实现websocket串行的全部流程,适用于任何类似于发布订阅模式类型。关键点就是在发送ws事件时声明一个promise,该promise的完成时机是由接收到服务端响应的message事件触发

推荐文章

推荐如下文章|官网让你更加理解Promise底层debug底层以及debug和代码补全实现
手写promise源码实现
JavaScript Debugger 原理揭秘
Debug Adapter Protocol
Language Server Protocol