2023 年的 Web Worker 项目实践

这篇具有很好参考价值的文章主要介绍了2023 年的 Web Worker 项目实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

引入 Web Worker

Worker 实践

Worker 到底有多难用

类库调研

有类库加持的 worker 现状

向着舒适无感的 worker 编写前进

1. 抽取依赖,管理编译和更新:

2. 定义公共调用函数,引入所打包的依赖并串联流程:

3. 优化语法支持

4. 其他问题

总结

参考资料


前言

    Web Workers 是 2009 年就已经提案的老技术,但是在很多项目中的应用相对较少,常见一些文章讨论如何写 demo ,但很少有工程化和项目级别的实践,本文会结合 Web Workers 在京东羚珑的程序化设计项目中的实践,分享一下在当下的 2023 年,关于 worker 融入项目的一些思考和具体的实现方式,涉及到的 demo 已经放在 github 上附在文末,可供参考。

        先简单介绍下 Web Workers,它是一种可以运行在 Web 应用程序后台线程,独立于主线程之外的技术。众所周知,JavaScript 语言是单线程模型的,而通过使用 Web Workers,我们可以创造多线程环境,从而可以发挥现代计算机的多核 CPU 能力,在应对规模越来越大的 Web 程序时也有较多收益。

        Web Workers 宏观语义上包含了三种不同的 Worker:DedicatedWorker(专有worker)、 SharedWorker(共享Worker)、 ServiceWorker,本文讨论的是第一种,其他两种大家可以自行研究一下。

引入 Web Worker

当引入新技术时,通常我们会考虑的问题有:

        1、兼容性如何?

        2、使用场景在哪?

问题 1,Web Workers 是 2009 年的提案,2012 年各大浏览器已经基本支持,11 年过去了,现在使用已经完全没有问题啦

2023 年的 Web Worker 项目实践

问题 2,主要考虑了以下 3 点:

  1. ·Worker API 的局限性:同源限制、无 DOM 对象、异步通信,因此适合不涉及 DOM 操作的任务

  2. ·Worker 的使用成本:创建时间 + 数据传输时间;考虑到可以预创建,可以忽略创建时间,只考虑数据传输成本,这里可参考 19 年的一个测试 Is postMessage slow[1] ,简要结论是比较乐观的,大部分设备和数据情况下速度不是瓶颈

  3. ·任务特点:需要是可并行的多任务,为了充分利用多核能力,可并行的任务数越接近 CPU 数量,收益会越高。多线程场景的收益计算,可以参考 Amdahl 公式,其中 F 是初始化所需比例,N 是可并行数:

    2023 年的 Web Worker 项目实践

综上结论是,可并行的计算密集型任务适合用 Worker 来做。

不过 github 上我搜罗了一圈,也发现有一些不局限于此,颇有创意的项目,供大家打开思路:

  1. ·redux 挪到了 worker 内[2]

  2. ·dom 挪到了 worker 内[3]

  3. ·可使用多核能力的框架[4]

Worker 实践

        介绍完 worker ,一个问题出现了:为什么一个兼容性良好,能够发挥并发能力的技术(听起来很有诱惑力),到现在还没有大规模使用呢?

        我理解有 2 个原因:一是暂无匹配度完美的使用场景,因此引入被搁置了;二是 worker api 设计得太难用,参考很多 demo 看,限制多配置还麻烦,让人望而却步。本文会主要着力于第二点,希望给大家的 worker 实践提供一些成熟的工程化思路。

        至于第一点理由,在如此卷的前端领域,当你手中已经有了一把好用的锤子,还找不到那颗需要砸的钉子吗?

Worker 到底有多难用

下面是一个原始 worker 的调用示例,上面是主线程文件,下面是 worker 文件:

// index.js
const worker = new Worker('./worker.js')
worker.onmessage = function (messageEvent) {
  console.log(messageEvent)
}
// worker.js
importScripts('constant.js')
function a() {
  console.log('test')
}

其中问题有:

  1. ·postMessage 传递消息的方式不适合现代编程模式,当出现多个事件时就涉及分拆解析和解决耦合问题,因此需要改造

  2. ·新建 worker 需要单独文件,因此项目内需要处理打包拆分逻辑,独立出 worker 文件

  3. ·worker 内可支持定义函数,可通过importScript 方式引入依赖文件,但是都独立于主线程文件,依赖和函数的复用都需要改造

  4. ·多线程环境必然涉及同步运行多个 worker,多 worker 的启动、复用和管理都需要自行处理

看完这么多问题,有没有感觉头很大,一个设计这样原始的 api,如何舒服的使用呢?

类库调研

        首先可以想到的就是借助成熟类库的力量,下面表格是较为常见的几款 worker 类库,其中我们可能会关注的关键能力有:

  1. ·通信是否有包装成更好用的方式,比如 promise 化或者 rpc 化

  2. ·是否可以动态创建函数——可以增加 worker 灵活性

  3. ·是否包含多 worker 的管理能力,也就是线程池

  4. ·考虑 node 的使用场景,是否可以跨端运行

2023 年的 Web Worker 项目实践

        比较之下,workerpool[5] 胜出,它也是个年纪很大的库了,最早的代码提交在 6 年前,不过实践下来没有大问题,下文都会在使用它的基础上继续讨论。

有类库加持的 worker 现状

        通过使用 workerpool,我们可以在主线程文件内新建 worker;它自动处理多 worker 的管理;可以执行 worker 内定义好的函数 a;可以动态创建一个函数并传入参数,让 worker 来执行。

// index.js
import workerpool from 'workerpool'
const pool = workerpool.pool('./worker.js')
// 执行一个 worker 内定义好的函数
pool.exec('a', [1, 2]).then((res) => {
  console.log(res)
})
// 执行一个自定义函数
pool
  .exec(
    (x, y) => {
      return x + y
    }, // 自定义函数体
    [1, 2], // 自定义函数参数
  )
  .then((res) => {
    console.log(res)
  })
// worker.js
importScripts('constant.js')
function a() {
  console.log('test')
}

但是这样还不够,为了可以舒适的写代码,我们需要进一步改造。

向着舒适无感的 worker 编写前进

我们期望的目标是:

  1. ·足够灵活:可以随意编写函数,今天我想计算1+1,明天我想计算1+2,这些都可以动态编写,最好它可以直接写在主线程我自己的文件里,不需要我跑到 worker 文件里去改写;

  2. ·足够强大:我可以使用公共依赖,比如 lodash 或者是项目里已经定义好的某些公共函数。

        考虑到 workerpool 具备了动态创建函数的能力,第一点已经可以实现;而第二点关于依赖的管理,则需要自行搭建,接下来介绍搭建步骤。

1. 抽取依赖,管理编译和更新:

        新增一个依赖管理文件worker-depts.js,可按照路径作为 key 名构建一个聚合依赖对象,然后在 worker 文件内引入这份依赖

// worker-depts.js
import * as _ from 'lodash-es'
import * as math from '../math'

const workerDepts = {
  _,
  'util/math': math,
}

export default workerDepts
// worker.js
import workerDepts from '../util/worker/worker-depts'

2. 定义公共调用函数,引入所打包的依赖并串联流程:

    worker 内定义一个公共调用函数,注入 worker-depts 依赖,并注册在 workerpool 的方法内

// worker.js
import workerDepts from '../util/worker/worker-depts'

function runWithDepts(fn: any, ...args: any) {
  var f = new Function('return (' + fn + ').apply(null, arguments);')
  return f.apply(f, [workerDepts].concat(args))
}

workerpool.worker({
  runWithDepts,
})

主线程文件内定义相应的调用方法,入参是自定义函数体和该函数的参数列表

// index.js
import workerpool from 'workerpool'
export async function workerDraw(fn, ...args) {
  const pool = workerpool.pool('./worker.js')
  return pool.exec('runWithDepts', [String(fn)].concat(args))
}

        完成以上步骤,就可以在项目任意需要调用 worker 的位置,像下面这样。自定义函数内容,引用所需依赖(已注入在函数第一个参数),进行使用了。

        这里我们引用了一个项目内的公共函数 fibonacci,也引用了一个 lodash 的 map 方法,都可以在depts 对象上取到

// 项目内需使用worker时
const res = await workerDraw(
  (depts, m, n) => {
    const { map } = depts['_']
    const { fibonacci } = depts['util/math']
    return map([m, n], (num) => fibonacci(num))
  },
  input1,
  input2,
)

3. 优化语法支持

        没有语法支持的依赖管理是很难用的,通过对 workerDraw 进行 ts 语法包装,可以实现在使用时的依赖提示:

import workerpool from 'workerpool'
import type TDepts from './worker-depts'

export async function workerDraw<T extends any[], R>(fn: (depts: typeof TDepts, ...args: T) => Promise<R> | R, ...args: T) {
  const pool = workerpool.pool('./worker.js')
  return pool.exec('runWithDepts', [String(fn)].concat(args))
}

然后就可以在使用时获取依赖提示:

2023 年的 Web Worker 项目实践

4. 其他问题

        新增了 worker 以后,出现了 window和 worker 两种运行环境,如果你恰好和我一样需要兼容 node 端运行,那么运行环境就是三种,原本我们通常判断 window 环境使用的也许是 typeof window === 'object'这样,现在不够用了,这里可以改为 globalThis 对象,它是三套环境内都存在的一个对象,通过判断globalThis.constructor.name的值,值分别是'Window' / 'DedicatedWorker'/ 'Object',从而实现环境的区分

总结

        通过使用 workerpool,添加依赖管理和构建公共 worker 调用函数,我们实现了一套按需调用,灵活强大的 worker 使用方式。

        在京东羚珑的程序化设计项目中,通过把 skia 图形绘制部分逐步改造为 worker内调用,我们实现了整体服务耗时降低 75% 的效果,收益还是非常不错的。

        文中涉及的代码示例都已放在 github[6] 上,内有 vite 和 webpack 两个完整实现版本,感兴趣的小伙伴可以 clone 下来参照着看~

参考资料


[1] Is postMessage slow: https://dassur.ma/things/is-postmessage-slow/

[2] redux 挪到了 worker 内: https://blog.axlight.com/posts/off-main-thread-react-redux-with-performance

[3] dom 挪到了 worker 内: https://github.com/ampproject/worker-dom

[4] 可使用多核能力的框架: https://github.com/neomjs/neo

[5] workerpool: https://github.com/josdejong/workerpool

[6] github: https://github.com/Silencesnow/worker-demo-2022

[7] MDN Web Workers API: https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API

[8] workerpool: https://github.com/josdejong/workerpool

[9] 前端项目上 Web Worker 实践: https://www.youtube.com/watch?v=AEpG-3XXrjk

[10] Web Worker 文献综述: https://juejin.cn/post/6854573213297410062文章来源地址https://www.toymoban.com/news/detail-464918.html

到了这里,关于2023 年的 Web Worker 项目实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 实践解决:IDEA2022版本创建Maven项目时没有出现src目录

    大家好,我是小白DunkIT,今天给大家分享一下我最近遇到的IDEA2022.3版本创建Maven项目时出现没有src目录的问题。 问题:IDEA创建Maven项目时没有出现src目录 创建Maven项目 新版本的IDEA创建是选用的是Maven Archetype,选择这个也是和Maven一样的。按照这样流程创建完成之后的的架构

    2024年02月11日
    浏览(65)
  • 如何设计一个优秀的 Go Web 项目目录结构

    Go 语言作为一门高效、简洁、并发安全的语言,越来越受到开发者们的青睐,特别是在 Web 开发及云原生领域。而对于一个大型的 Go Web 项目而言,一个优秀的目录结构设计是必不可少的。它可以帮助我们更好地组织代码、减少冗余、提高可维护性和可扩展性。 在本文中,我

    2024年02月12日
    浏览(96)
  • 解决微信小程序“app.json: [“workers“] 字段需为 目录“错误及worker的使用

    我们如果遇到[ miniprogram/app.json 文件内容错误] miniprogram/app.json: [“workers”] 字段需为 目录(env: Windows,mp,1.06.2209190; lib: 2.21.4)这样的错误,那么一定是我们在app.json中定义了这个字段。 1.方法一: 找到这个字段直接删除即可。 2.方法二: 在你的项目目录中补充一个对应的worker

    2024年02月12日
    浏览(43)
  • 梳理 Web Worker 及实战场景

    有一些前端技术点,即使以前用过,但没有自己动手归纳总结过,许久还是要回过头来还是需要重新梳理。于是,本文就来梳理一下 Web Worker。 由于JavaScript语言采用的是单线程,同一时刻只能做一件事,如果有多个同步计算任务执行,则在这段同步计算逻辑执行完之前,它下

    2024年02月14日
    浏览(40)
  • JavaScript Web Worker用法指南

    🧑‍🎓 个人主页: 《爱蹦跶的大A阿》 🔥 当前正在更新专栏: 《VUE》 、《JavaScript保姆级教程》、《krpano》 ​  ​         Web Worker可以将耗时任务放到后台执行,避免阻塞UI。本文将详细介绍Web Worker的用法,让你可以提升web应用性能。 通过本文你将学习: Web Worker的工作

    2024年01月18日
    浏览(33)
  • HTML5 Web Worker

    HTML5 Web Worker是一种浏览器提供的JavaScript多线程解决方案,它允许在后台运行独立于页面主线程的脚本,从而避免阻塞页面的交互和渲染。Web Worker可以用于执行计算密集型任务、处理大量数据、实现并行计算等,从而提升前端应用的性能和响应能力。 特点和用途: 多线程:

    2024年02月14日
    浏览(33)
  • Web Worker的概念、用法、使用场景

    ​ 目录 1. 简介 2. 适用场景 2.1 复杂计算 2.2 后台下载 2.3 数据处理 2.4 实时通信 3. 代码示例 3.1 Worker特性检测 3.2 Worker API 3.3 SharedWorker API 3.4 创建 JavaScript 文件 3.5 创建 Web Worker 4. 总结 Web Worker  使得在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作成为可

    2024年02月08日
    浏览(30)
  • 如何使用 Web Worker 处理大文件上传

    博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接 : 🔗 精选专栏 : 《面试题大全》 — 面试准备的宝典! 《IDEA开发秘籍》 — 提升你的IDEA技能! 《100天精通Golang》 — Go语言学习之旅! 领域矩阵 : 🌐 猫头虎技术领域矩阵 : 深入探索

    2024年01月23日
    浏览(38)
  • 【2023年的就业形势依旧严峻】

    2023年口罩放开的第一年,也是第一个招聘会,挤满了求职者和用人单位,大多数都是想着重新开始,抓住金三银四的好时机,找到心仪的工作和符合岗位要求的人才,一起整装出发。我们理想的状态是,经济已经在恢复了,那么就业市场也应该恢复了。然而现实是残酷的,海

    2024年02月04日
    浏览(35)
  • html5学习笔记18-web存储、web sql、web worker

    https://www.runoob.com/html/html5-webstorage.html HTML5 web 存储,可替代 cookie 的本地存储方式。 客户端存储数据的两个对象:localStorage网站的 sessionStorage窗口的 web db sql web worker

    2024年02月10日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包