在 Web Worker 或 Service Worker 中运行 WebSocket - javascript

2024-04-19

我有来自不同站点的 9 个 websocket 连接,用于使用数据更新 DOM。目前,我正在连接所有网络套接字并监听所有网络套接字,并通过函数调用更新数据。

我面临的问题是有很多 websocket 连接,并且存在内存和 CPU 使用问题。如何使用 Service Worker 和 Web Worker 来优化如此多的 WebSocket 连接?

async function appendGatePublicTickersData(e) {
  if (e.event == "update" && e.result[0].contract == "BTC_USD") {
    if ('total_size' in e.result[0]) {
      $(".gate-btc-open-interest").html(commaNumber(e.result[0].total_size))
      if ('last' in e.result[0]) {
        $(".gate-btc-open-value").html(commaNumber(customFixedRounding((e.result[0].total_size / e.result[0].last), 4)))
      }
    }

    if ('volume_24h_usd' in e.result[0]) {
      $(".gate-btc-24-volume").html(commaNumber(e.result[0].volume_24h_usd))
    }

    if ('volume_24h_btc' in e.result[0]) {
      $(".gate-btc-24-turnover").html(commaNumber(e.result[0].volume_24h_btc))
    }

    if ('funding_rate' in e.result[0]) {
      var fundingRateBtcGate = customFixedRounding(e.result[0].funding_rate * 100, 4)
      $(".public-gate-btc-funding").html(fundingRateBtcGate)
    }

    if ('funding_rate_indicative' in e.result[0]) {
      var predictedRateBtcGate = customFixedRounding(e.result[0].funding_rate_indicative * 100, 4)
      $(".public-gate-btc-predicted").html(predictedRateBtcGate)
    }
  }
}

var pubGateWs = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/btc");

pubGateWs.addEventListener("open", function() {
  pubGateWs.send(JSON.stringify({
    "time": 123456,
    "channel": "futures.tickers",
    "event": "subscribe",
    "payload": ["BTC_USD", "ETH_USD"]
  }))
});

pubGateWs.addEventListener("message", function(e) {
  e = JSON.parse(e.data)
  appendGatePublicTickersData(e)
});

pubGateWs.addEventListener("close", function() {});

由于您正在使用 Web 套接字,因此最好使用SharedWorker https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker为您的 Web Sockets 创建一个新线程。和正常人的区别WebWorker and a SharedWorker加载页面时,Web Worker 将在每个选项卡或浏览器中创建一个新会话,而共享 Worker 将在每个选项卡中使用相同的会话。因此,您的所有选项卡或窗口都将具有相同的工作线程和相同的 Web Socket 连接来使用。

如果数据更新非常频繁(每秒超过 60 次)并且每次都需要更新 DOM,那么使用requestAnimationFrame https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame方法来限制 DOM 的更新量。它将等待下一个重绘周期,然后再用新内容更新 DOM,大约每秒 60 次,即 60FPS。

其实现如下例所示:

主线程。

// Create shared worker.
const webSocketWorker = new SharedWorker('web-sockets-worker.js');

/**
 * Sends a message to the worker and passes that to the Web Socket.
 * @param {any} message 
 */
const sendMessageToSocket = message => {
  webSocketWorker.port.postMessage({ 
    action: 'send', 
    value: message,
  });
};

// Event to listen for incoming data from the worker and update the DOM.
webSocketWorker.port.addEventListener('message', ({ data }) => {
  requestAnimationFrame(() => {
    appendGatePublicTickersData(data);
  });
});
  
// Initialize the port connection.
webSocketWorker.port.start();

// Remove the current worker port from the connected ports list.
// This way your connectedPorts list stays true to the actual connected ports, 
// as they array won't get automatically updated when a port is disconnected.
window.addEventListener('beforeunload', () => {
  webSocketWorker.port.postMessage({ 
    action: 'unload', 
    value: null,
  });

  webSocketWorker.port.close();
});

共享工作者。

/**
 * Array to store all the connected ports in.
 */
const connectedPorts = [];

// Create socket instance.
const socket = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/btc");

// Send initial package on open.
socket.addEventListener('open', () => {
  const data = JSON.stringify({
    "time": 123456,
    "channel": "futures.tickers",
    "event": "subscribe",
    "payload": ["BTC_USD", "ETH_USD"]
  });

  socket.send(data);
});

// Send data from socket to all open tabs.
socket.addEventListener('message', ({ data }) => {
  const payload = JSON.parse(data);
  connectedPorts.forEach(port => port.postMessage(payload));
});

/**
 * When a new thread is connected to the shared worker,
 * start listening for messages from the new thread.
 */
self.addEventListener('connect', ({ ports }) => {
  const port = ports[0];

  // Add this new port to the list of connected ports.
  connectedPorts.push(port);

  /**
   * Receive data from main thread and determine which
   * actions it should take based on the received data.
   */
  port.addEventListener('message', ({ data }) => {
    const { action, value } = data;

    // Send message to socket.
    if (action === 'send') {
      socket.send(JSON.stringify(value));

    // Remove port from connected ports list.
    } else if (action === 'unload') {
      const index = connectedPorts.indexOf(port);
      connectedPorts.splice(index, 1);
    }
  });

  // Start the port broadcasting.
  port.start();
});

旁注:你的appendGatePublicTickersData不使用await关键字,因此它不必是async功能。


从 Safari 16 开始支持。
浏览器对共享 Web Worker 的支持 https://caniuse.com/sharedworkers

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Web Worker 或 Service Worker 中运行 WebSocket - javascript 的相关文章

随机推荐

  • android在picasso加载后获取Drawable图像

    我正在使用 Picasso 库从 url 加载图像 我使用的代码如下 Picasso with getContext load url placeholder R drawable placeholder error R drawable
  • 如何将子集合添加到 Firebase Cloud Firestore 中的文档

    该文档没有任何有关如何将子集合添加到文档的示例 我知道如何将文档添加到集合以及如何将数据添加到文档 但是如何将集合 子集合 添加到文档 难道不应该有这样的方法 dbRef document example addCollection sub
  • 创建 UI 设计师

    我想使用 Silverlight WPF 或 WinForms 为自定义系统创建一个 UI 设计器 解决这个问题的最佳方法是什么 我应该注意哪些命名空间 SDK 等 我建议你看看sharpdevelophttp www icsharpcod
  • setTimeout 会使用 Heroku 免费的 dyno 小时吗?

    我有一个机器人 它的命令允许用户输入一条用破折号分隔的消息 然后输入指定的时间 然后将其传递给机器人 机器人在指定的时间过去后用该消息提醒用户 function reminder msg const message msg content
  • 如何使用位码选项制作胖框架?

    环境 XCode 7 0 1 模块 Objective C 捆绑类型 框架 你好 我正在尝试创建一个支持armv7 armv7s arm64 i386和x86 64的框架 我正在使用聚合来制作脂肪库 在聚合脚本中 我正在运行两个 xcode
  • VS Code - 错误:EPERM:不允许操作

    在 Windows 10 中使用 VS code 时 我遇到了很多权限问题 When trying to move a folder 错误 EPERM 不允许操作 重命名 路径 a gt 路径 b When deleting a folde
  • 等待 Firestore 查询完成

    我目前正在尝试在 firestore 中运行多个查询 并希望等待它们全部完成后再执行下一个代码 我已经阅读了几种可能的途径 但还没有找到一个好的 Android 示例 public HashMap
  • 单击项目菜单时如何关闭抽屉菜单?

    这是点击事件 view setOnTouchListener new View OnTouchListener Override public boolean onTouch View v MotionEvent event v setBa
  • 为什么使用分号? [复制]

    这个问题在这里已经有答案了 除了主观视觉感知和同一行有多个语句的情况之外 是否有任何理由在 JavaScript 中的语句末尾使用分号 看起来有大量证据表明分号的使用是高度可选的 并且只有少数特定情况需要使用分号 因为当 JavaScrip
  • FQL Multiquery 编写联接查询

    简单的说 我需要帮助构建 FQL 多查询请求 该请求将执行以下操作 然后从登录用户的好友列表中获取 UID 使用这些 ID 获取过去两周的所有评论和消息 来自流 最后 将这些结果与用户名连接起来 这是我迄今为止的疑问 1 GRAB UID
  • with(nolock) 或 (nolock) - 有区别吗?

    一切都基于 with nolock 完全适合这种情况的假设 已经有很多问题在争论是否使用 with nolock 我环顾四周 无法找到使用之间是否存在实际差异with nolock select customer zipcode from
  • 如何使用 jQuery 验证插件验证正则表达式?

    如何使用 jQuery 验证插件验证正则表达式 在此代码中 我们可以使用一个 jQuery 插件 jQuery Validation 插件来验证表单 但我不知道如何使用此插件来验证正则表达式
  • 排序在非字母(即亚洲)语言中意味着什么?

    我有一些代码可以按对象属性对表列进行排序 我想到 在日语或中文 非字母语言 中 发送到排序函数的字符串将以字母语言的方式进行比较 以日本姓氏列表为例 寿拘 Suzuki 松坂 Matsuzaka 松井 Matsui 山田 Yamada 藤本
  • 窗口中的图标属性导致 Windows XP SP2 中的应用程序崩溃

    假设我有这个
  • 如何将 JFileChooser 限制为自定义文件类型? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我遇到了一些小问题JFileChooser 我想限制只读取某些类型的文件 另一个堆栈答案建议使用File
  • 每次项目运行时,带有主菜单的窗体都会缩小

    每当我构建以下程序时Form1的大小缩小了20 有些情况我需要对表格做一些改变 比如移动someLaggyControl1几个像素 但并非总是如此 这很奇怪 该表格使用了MainMenu https msdn microsoft com e
  • 这个 PHP(函数/构造?)有什么作用,在哪里可以找到更多关于它的文档?

    简单的问题 这是这段代码 r rand 0 1 c r 0 rand 65 90 rand 97 122 inputpass chr c 我明白它的作用end结果 但我想要更好地解释它是如何工作的 这样我就可以自己使用它 抱歉 如果这是一个
  • 获取“TypeError:schema.virtual(...).get 不是函数”

    当我尝试定义架构时出现此错误 Error node modules mongoose lib plugins idGetter js 12 schema virtual id get idGetter TypeError schema vi
  • 主流编译器是否将引用传递基本类型转换为复制传递?

    通过引用传递对象是向其传递地址的更简单 更快且更安全的方法 但对于大多数编译器来说 都是一样的 引用实际上是指针 现在基本类型怎么样int 将地址传递给int在函数内部使用它会比通过复制传递它慢 因为在使用之前需要取消引用指针 现代编译器如
  • 在 Web Worker 或 Service Worker 中运行 WebSocket - javascript

    我有来自不同站点的 9 个 websocket 连接 用于使用数据更新 DOM 目前 我正在连接所有网络套接字并监听所有网络套接字 并通过函数调用更新数据 我面临的问题是有很多 websocket 连接 并且存在内存和 CPU 使用问题 如