什么,你还不会WebSocket 的异步 close 方法?

前言

彦祖们,阅读本文前,希望你对 WebSocket 有基础的了解

业务场景复现

笔者在最近的项目中接手了这样一个坑

笔者用最简单的代码,模拟了一下这个场景

大致意思是先创建了一个 ws, 500ms 后关闭这个 ws 同时又新建了一个 ws

js 复制代码
let status = 'open'
let ws = null
function closeWs(){
  ws.close()
}

function createWs(){
  ws = new WebSocket('ws://localhost:8080')
  ws.onopen = ()=>{
    status = 'open'
  }
  ws.onclose = ()=>{
    status = 'close'
  }
}

function initWs(){
  closeWs()
  createWs()
}

createWs()
setTimeout(()=>{
  initWs()
},500)

乍眼一看,这代码的确是没什么问题,其实线上也已经稳定运行了一年多了

但是最近有一家客户却反映了这个问题

ws 明明是 open 的,但是界面上显示的却是 close 的

定位问题

其实这种问题也比较好定位,仔细观察一下 onclose onopen 都是回调函数

那么如果 closeWs 触发的onclose的回调比 createWs 触发的onopen 回调更晚执行的话

就会导致这个 bug

解决问题

这个问题解决的方式有好多种,但是今天我们的主题是封装一个异步 close 方法

我们期待的使用方式

js 复制代码
ws.asyncClose().then(res=>{
  // 成功关闭后的逻辑
})

开始封装

挂载 asyncClose 方法

我们给 WebSocket 原型上挂载一个异步关闭方法 asyncClose

但是笔者不是很建议操作原型(比如说之后 WebSocket 新出了一个同名方法,就会产生 bug)

本文只做技术讲解,彦祖们可以封装一个自己的类 extends 一下 WebSocket

好了,废话不多说,看下此时的函数签名

js 复制代码
// code reason 即源生 ws 的 code reason
WebSocket.prototype.asyncClose = function(code,reason){
  
}

实现 asyncClose 方法

既然我们是异步关闭,那么自然会返回一个 promise

笔者在这一步遇到两个难点

1.如何在方法内部获取 ws 实例?

2.promise 完成的理想时机?

不断实践,给出方案

问题 1

既然我们调用的方式是 ws.asyncClose,那么对应的方法内部关键字 this 不就是 ws吗?

问题 2

完成的时机非常明朗,需要在 onclose函数触发,那么在asyncClose内部,我们只需要做赋值不做调用

核心代码

有了方案,代码也自然非常清晰了

js 复制代码
WebSocket.prototype.asyncClose = function(code,reason){
  // 此处一定要保证 this 的准确性
  const ws = this
  ws.close(code,reason)
  return new Promise(resolve=>{
    ws.resolve = resolve // 只做赋值, 需要在 onclose 回调中执行 resolve
  })
}

完整代码

让我们用封装的方法来测试一下,调用方式也非常简单

js 复制代码
WebSocket.prototype.asyncClose = function(code,reason){
  // 此处一定要保证 this 的准确性
  const ws = this
  ws.close(code,reason)
  return new Promise(resolve=>{
    ws.resolve = resolve // 只做赋值, 需要在 onclose 回调中执行 resolve
  })
}

let status = 'open'

function createWs(){
  ws = new WebSocket('ws://localhost:8080')
  ws.onopen = ()=>{
    status = 'open'
  }
  ws.onclose = (e) => {
    status = 'close'
    ws.resolve?.(e)
    ws.resolve = null
  }
}

function initWs(){
  ws.asyncClose().then(createWs) // 保证关闭完成后 再创建
}

createWs()
// 模拟 500ms 后先关闭再打开
setTimeout(()=>{
  initWs()
},500)

缺陷分析

这种方式有一个缺陷,那就是需要用户在 onclose 回调中手动调用 resolve 方法

不过处理方式也非常简单,彦祖们只需要自己封装一个类,把这些公共逻辑都封装起来就行了

评论区如果呼声比较高,下期就带彦祖们一起封装一个支持异步 close 的 WebSocket 类吧~

写在最后

实际业务场景中,也是小概率会遇到这种 bug, 算是一个比较难复现的 bug 吧

但是彦祖们,因此而收获的技术积累却是可遇而不可求的,共勉!

感谢彦祖们的阅读

个人能力有限

如有不对,欢迎指正 🌟 如有帮助,建议小心心大拇指三连🌟

相关推荐
福兮说10 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理11 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆66612 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人12 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)12 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan12 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_13 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒13 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技13 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu14 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub