前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Web Workers RPC

Web Workers RPC

作者头像
奋飛
发布2022-10-05 18:38:36
6390
发布2022-10-05 18:38:36
举报
文章被收录于专栏:Super 前端Super 前端

CSDN话题挑战赛第2期

参赛话题:前端技术分享

说在前面

对于需要花费大量时间才能处理的任务,javascript 引擎通常会有两种现象:

  1. 执行当前任务花费大量的时间,使得无法执行任何其他操作,导致浏览卡顿
  2. 如果此时回调队列被阻塞的任务过多时,大多数浏览器都会抛出一个提示信息,征求是否要关闭网页

那么,我们如何在不阻塞UI并使浏览器正常响应的情况下执行繁重的代码呢?

引言

javascript 是单线程编程语言,这使得我们开发过程中不必关注因多线程导致的复杂场景(如,死锁)。

单线程意味着某一时刻只能做一件事情! javascript 引擎,以最常见的 v8 举例,内置了 事件循环 Event Loop + 回调队列 Callback Queue 机制,以及通过宏任务 macrotask + 微任务 microtask 来分配执行优先级,来确保高效运行。

因此,解决上述问题,通常有两种方案:

  1. 异步回调(asynchronous callbacks):依赖第三方服务
  2. 开启多线程(web worker):本文重点,浏览器提供了相应 web api

关于「JavaScript的工作原理」「Event loop及macrotask & microtask」相关内容,可阅读下述文章:

Web Workers

worker 的一个优势在于能够执行处理器密集型的运算而不会阻塞 UI 线程。

web workers 浏览器整体兼容性很好,为我们大面积使用奠定了基础~~~

在一个 worker 中最主要的是不能直接影响父页面,包括操作父页面的节点以及使用页面中的对象。只能间接地实现,通过 DedicatedWorkerGlobalScope.postMessage 回传消息给主脚本,然后从主脚本那里执行操作或变化。

worker 的优势明显,但在通信上的处理极其繁琐,导致大家使用的频次并不高。

Comlink 解决了通信的问题,其借助 Proxy 可以忽略所有繁琐的通信细节(无需考虑事件订阅所带来的复杂性),极大降低了 Worker 的维护成本。-- RPC方式

RPC 全称是 Remote Procedure Call,即远程过程调用。目的是:让我们调用远程方法像调用本地方法一样,无需了解底层网络技术的协议等。

案例

地址:https://github.com/381510688/practice/tree/master/web-api-test

传统写法

// index.js
const worker = new Worker('worker.js')
comput.addEventListener('click', function () {
  worker.postMessage({
    num1: num1.value,
    num2: num2.value
  })
})
worker.onmessage = function (msgEvent) {
  res.innerHTML = msgEvent.data
}

// worker.js
onmessage = function (msgEvent) {
  let {num1, num2} = msgEvent.data
  postMessage(Number(num1) + Number(num2))
}

尝试保留 add 方法

// index.js
const worker = new Worker('worker.js')
comput.addEventListener('click', function () {
  worker.postMessage('ok')
})
worker.onmessage = function (msgEvent) {
  res.innerHTML = msgEvent.data.add(num1.value, num2.value)
}

// worker.js
function add (num1, num2) {
  return Number(num1) + Number(num2)
}
onmessage = function (msgEvent) {
  postMessage({add: add})
}

UncaughtDOMException: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': function add (num1, num2) {}

Worker.postMessage() Worker 接口的 postMessage()方法向worker的内部作用域发送一个消息。接受单个参数(要发送给worker的数据)。数据可以是由结构化克隆算法处理的任何值或JavaScript对象,其包括循环引用。 结构化克隆所不能做到的:

  • Error 以及 Function 对象是不能被结构化克隆算法复制的;如果你尝试这样子去做,这会导致抛出 DATA_CLONE_ERR 的异常。
  • 企图去克隆 DOM 节点同样会抛出 DATA_CLONE_ERR 异常。
  • 对象的某些特定参数也不会被保留
    • RegExp 对象的 lastIndex 字段不会被保留
    • 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write,因为这是默认的情况下。
    • 原形链上的属性也不会被追踪以及复制。

comlink 示例

// index.js
const worker = new Worker("worker.js");
const cw = Comlink.wrap(worker);
comput.addEventListener('click', async function () {
  let result = await cw(num1.value, num2.value)
  res.innerHTML = result
})

// worker.js
importScripts("https://unpkg.com/comlink/dist/umd/comlink.js");
function add (num1, num2) {
  return Number(num1) + Number(num2)
}
Comlink.expose(add);

本质上依然是 MessagePort 消息通讯,不过封装了我们所头疼的“操作判断”,并以一种更优雅的方式(Proxy + Promise)来处理。

Comlink 采用的 RPC 代理方式,并不是传递上下文环境(因为这是非常危险的,而且函数传递时会导致 Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Worker': xxx could not be cloned. 报错)。

RPC:Remote Procedure Call,远程过程调用,指调用不同于当前上下文环境的方法,通常可以是不同的线程、域、网络主机,通过提供的接口进行调用。

index.html

import * as Comlink from "https://unpkg.com/comlink/dist/esm/comlink.mjs"

const worker = new Worker("worker.js")
const cw = Comlink.wrap(worker)
const cpt = await new cw()
comput.addEventListener('click', async function () {
  let result = await cpt.add(num1.value, num2.value)
  dom.innerHTML = result
})

worker.js

importScripts("https://unpkg.com/comlink/dist/umd/comlink.js")
class Comput {
  constructor () {}
  add (num1, num2) {
    return Number(num1) + Number(num2);
  }
  sub (num1, num2) {
    return Number(num1) - Number(num2);
  }
}
Comlink.expose(Comput)

importScripts() 将一个或多个脚本同步导入到工作者的作用域中。隶属于:WorkerGlobalScope 接口。

注意:new Worker('worker.js') scriptURL will be fetched and executed in the background, creating a new global environment for which worker represents the communication channel. – https://html.spec.whatwg.org/multipage/workers.html#dom-worker-dev

总结

Comlink(RPC方式)使我们可以更多的关注业务内容,忽略调用(网络)细则。

客户端应用程序调用本地存根(stub),而不是调用实际代码;服务端应用程序接受参数,通过服务器存根(stub)检索实际代码进行运行。

链接

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 说在前面
  • 引言
  • Web Workers
  • 案例
    • 传统写法
      • comlink 示例
      • 总结
      • 链接
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档