uniapp小程序多线程 Worker 实战【2024】

需求

最近遇到个小程序异步解码的需求,采用了WebAssembly,涉及大量的计算。由于小程序的双线程模型只有一个线程处理数据,因此智能寻求其它的解决方案。查看小程序的文档,发现小程序还提供一个异步线程的Worker方案,可以并行的。于是尝试采用Worker来进行异步运算,看了下文档,貌似只能有一个Worker异步进行,但是聊胜于无,能多一个线程并行计算,页面逻辑不会卡住就已经很不错了。

由于本人采用uniapp来进行小程序开发,由于引入了uniapp编译,导致整个开发过程更加复杂,本文记录了本人采用uni-best框架使用Worker过程遇到的一排深坑以及爬坑方案

小广告

uni-best不愧为2024年最佳的uni-app开发框架,uniapp+vue3+ts+unocss+uni-helper,typescript语言,体验极致的开发效率。本次项目就是在unibest生成的项目里进行uniapp开发

unibest最好用的 uniapp 开发模板icon-default.png?t=N7T8https://codercup.github.io/unibest-docs/

整合过程

下面按照官方的整合过程走一遍

创建目录

在src项目下创建workers目录,并建立index.js。这里是坑A,注意小程序Worker的引入必须显式的指定为.js,因此即使ts能够自动的编译为js,但是由于书写的原因。index文件必须为javascript而不是typeScript,但是index.js再引入的文件,是可以使用typescript格式的

引入文件

在页面采用一个按钮,点击开始进行异步的计算。按钮点击的代码如下:

在App.vue onShow里初始化

onShow(() => {const createNewWorker = () => wx.createWorker('workers/index.js', { useExperimentalWorker: true }) // 开启编码多线程let worker = createNewWorker()worker.onProcessKilled(() => {// 重新创建一个workerworker = createNewWorker()})
})

按照微信的文档,在某些情况下异步线程会被系统杀死。因此在这里采用了开启useExperimentalWorker保活机制

编写调用

下面按照微信官方的说明结合本人的项目开始编写

异步线程接收事件

下一步开始编写index.js,开启异步线程接口

worker.onMessage((obj) => {if (obj.event === 'add') {worker.postMessage({ event: 'addResult', data: obj.data.a + obj.data.b })}
})

解释下为什么这样写:

因为worker的调用是采用统一的调用接口,因此需要设计自己的消息格式,本人的消息格式设计如下

export interface IWorkderMessage {event: stringparams: any
}

event承载不同的消息给Worker,这样Worker可以做不同的事情。这里的例子只使用一个简单的调用,把消息参数里的a和b在异步线程相加,然后返回给主线程相加的结果

主线程发起事件

主线程的调用,在本人的结构里是采用mitt全局消息模型的,这样在统一的入口注册后。任何单元代码的任何地方都可以随时对异步线程发起调用。

  utils.on(Global.CC_WORKER_MESSAGE, (data: IWorkderMessage) => {worker.postMessage(data)})

页面发起异步调用

function doWorker() {utils.emit(Global.CC_WORKER_MESSAGE, { event: 'add', data: { a: 2, b: 3 } })
}

按微信官方的说法,在worker.onMessage里打印到console,理应看到输出(实际不是)。姑且先不管运行的结果,我们先按微信官方文档说明把代码写完。

主线程接收异步线程结果

主线程同样是采用worker.onMessage来接收异步线程的返回结果。我们加入到startWorker方法里,写成这样

onShow(() => {const createNewWorker = () => wx.createWorker('workers/index.js', { useExperimentalWorker: true }) // 开启编码多线程let worker = createNewWorker()worker.onProcessKilled(() => {// 重新创建一个workerworker = createNewWorker()})worker.onMessage((obj: Record<string, any>) => {// 异步线程全局消息转发utils.emit(obj.event, obj.data)})})

这里的utils.emit是我引入mitt后的全局消息模式,这样可以把返回的消息通过全局消息模型转发到对应的页面里

说明下这里为什么obj类型用Record<string,any>而不是IWorkderMessage,因为在小程序定义的d.ts里,已经把类型定义为Record,因此只能这样写

然后在对应界面写个全局的事件接收,这里仅打印下接收结果

  utils.on('addResult', (c) => {console.log(`addResult is ${c}`)})

坑来了

坑B

[worker] Uncaught Error: module 'workers/index.js' is not defined, require args is 'workers/index.js'

看到这里本人起初也是一头雾水的,啥叫index.js没定义,需要index.js。经过了一圈排查,才发现。我的编译后的dist\dev\mp-weixin目录里,没有workers目录!心态炸了,这叫什么错误,其它的文件都在,为毛单对workers过不去?

时间一分一秒过去,经过数小时冷静后。突然想到一个问题,vue3默认开启了Tree Shaking来优化代码,是不因为编译优化不认识worker机制,把从workders入口开始的整个代码链给弄丢了呢?按腾讯文档说,worker代码独立运行,会自动从createWorker开始运行,实时不是TreeShaking不认这一套,没代码调用的模块全部扫出家门了呢。之前require引入代码也不认,TreeShaking也给弄丢了,估计也是一个德行。

想完说干就干,修改下workers/index.js,做个简单的默认导出

worker.onMessage((obj) => {if (obj.event === 'add') {worker.postMessage({ event: 'addResult', data: obj.data.a + obj.data.b })}
})export default 'workers'

然后在App.vue导入,啥其它都不干,就打印下,这下编译器应该认为该模块是有用的吧

import workers from '@/workers'
console.log(workers)

然后开启调试,内牛满面,workers目录出现了,遗憾的是,继续出现错误了

坑C

估计很多人爬到这里,就会爬不动了。小程序上还是显示错误

app.js错误:
 Error: module 'workers/index.js' is not defined, require args is './workers/index.js'

看起来错误和前面的一样,但是仔细看又不一样。前面的是worker报错,是在启动worker的时候找不到模块,这里是app.js错误,而且仔细看是./workers/index.js找不到。那这个'workers/index.js' is not defined又是哪门子毛病呢?

经过数小时排查,发现编译后的app.js有这样一句代码:

但是如果我修改为workers/index.js就直接编译报错了

在这个地方卡了数小时。各种方法试过,一气之下想既然导入不对,干脆不要导入算了。于是把编译后的app.js的c=require("./workers/index.js")直接修改为c="hahaha",然后直接导入小程序模拟器运行。竟然成功了!

也就是说,对于最终编译的app.js,如果我把坑B产生的代码在最终编译结果去掉的话,代码就可以正常运行了。TMD VUE,TMD编译器优化!!!

但是不能每次都这样每编一次手动改一次呀,还不得把人累死,于是有了下一步

自动处理导入

既然是在编译阶段处理,那么我们应该是可以通过插件解决的,例如scss等插件都是可以对最终结果进行处理。于是想自己写个插件,对于从没写过插件的我来说难度又上了一个等级,幸好有GPT帮助,在折磨一阵子GPT后,再参考下其他类似代码。于是有了这个插件:

图片里vite.config.ts里的代码(顶部记得import fs from 'node:fs'):

      process.env.UNI_PLATFORM === 'mp-weixin' && {name: 'fix-uni-app-workers',apply: 'build',async closeBundle() {const buildType = process.env.NODE_ENV === 'development' ? 'dev' : 'build'const filePath = path.resolve(__dirname, `./dist/${buildType}/mp-weixin/app.js`) // 由app.js引入,修复这个即可fs.readFile(filePath, 'utf8', (err, data) => {if (err) {console.error('Error reading file:', err)return}console.log(`patch ${filePath}`)const result = data.replace(/require\("\.\/workers\/[a-zA-z-_]+\.js"\)/g, '""')// 写回文件fs.writeFile(filePath, result, 'utf8', (err) => {if (err) {console.error('Error writing file:', err)}})console.log('uniapp 小程序 worker 补丁完毕')})}}

解释下这个插件干了啥。

它在判断微信编译时(留着以后H5可以用编译开关写页面的Worker)开启,对编译目标目录的app.js进行处理。因此你的引用代码必须写到app.js。即

import workers from '@/workers'
console.log(workers)

这个是写在App.vue的,写到其它文件别怪我没提醒

然后对生成的文件做替换,把里面所有引入的js文件入口

=require("./workers/XXXXX.js")都替换成了="",这样都是打印空字符串,不会报错

对于workders里其它文件,也需要在app.js里通过写console.log的 方式注册,否则还是会出诡异的require args报错,这个正则把所有workers里的引入都替换成了常量字符

这样uniapp使用小程序的Workers就可以正常工作了🎉🎉🎉

按钮调用:

function doWorker() {utils.emit(Global.CC_WORKER_MESSAGE, { event: 'add', data: { a: 2, b: 3 } })
}

日志打印:

功能已经正常!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/23188.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第25天|回溯

回溯part02 216. 组合总和 III /*** param {number} k* param {number} n* return {number[][]}*/ var combinationSum3 function(k, n) {// k个数字相加为n// 只能使用1-9// 每个数字只能使用一次// 不能重复 如 1 2 4 、 4 1 2 不可以let res [];backtracking(k, n, [], …

联想Y410P跑大模型

安装vs 2017 查看GPU版本 查看支持哪个版本的cuda windows cuda更新教程_cuda 12.0-CSDN博客 下载并安装cuda tookit 10.1 CUDA Toolkit 10.1 Update 2 Archive | NVIDIA Developer 找到下载的文件&#xff0c;安装 参考安装链接 Win10 Vs2017 CUDA10.1安装&#xff08;避坑…

Due to a bug fix in https://github.com/huggingface/transformers/pull/28687

错误&#xff1a; Due to a bug fix in https://github.com/huggingface/transformers/pull/28687 transcription using a multilingual Whisper will default to language detection followed by transcription instead of translation to English.This might be a breaking …

InnoDB存储引擎非常重要的一个机制--MVCC(多版本并发控制)

Mysql是如何实现隔离性的&#xff1f;&#xff08;锁MVCC&#xff09; 隔离性是指一个事务内部的操作以及操作的数据对正在进行的其他事务是隔离的&#xff0c;并发执行的各个事务之间不能相互干扰。隔离性可以防止多个事务并发执行时&#xff0c;可能存在交叉执行导致数据的不…

安全U盘和普通U盘有什么区别?

安全U盘&#xff08;也称为加密U盘或安全闪存驱动器&#xff09;与普通U盘肯定是有一些区别的&#xff0c;从字面意思上来看&#xff0c;就能看出&#xff0c;安全U盘是能够保护文件数据安全性的&#xff0c;普通U盘没这一些功能的&#xff0c;可随意拷贝文件&#xff0c;不防盗…

面试4:c++(数位物联)

1.const 关健字的作用 定义常量&#xff0c;防止变量被意外修改&#xff0c;增强程序的可读性和维护性。 可以用于指针&#xff0c;声明指向常量的指针或常量指针。 2.static关健字的作用 (1)在函数内&#xff0c;用于修饰局部变量&#xff0c;使其生命周期延长到整个程序运行期…

mybatisplus多数据源内置方法报Invalid bound statement (not found)

在用mybatis-plus多数据源时用mapper内置的 selectList(queryWrapper) 查询数据报org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 问题是在配置多数据源时用的是SqlSessionFactoryBean&#xff0c;改为MybatisSqlSessionFactoryBean即可…

Python怎么逐行处理文件:深度解析与实用技巧

Python怎么逐行处理文件&#xff1a;深度解析与实用技巧 在Python中&#xff0c;逐行处理文件是一项常见且重要的任务。无论是读取大型日志文件、分析文本数据还是处理配置文件&#xff0c;逐行读取都能帮助我们更有效地管理内存并提高处理速度。本文将详细介绍Python中逐行处…

一文了解UVLED线光源的应用

在机器视觉系统中&#xff0c;光源作为不可或缺的一部分&#xff0c;能够提高目标成像效果&#xff0c;增强检测效果。光源的选择至关重要&#xff0c;选到不合适的会影响成像及检测效果。针对不同的检测对象,不同的形状光源应运而生。我们来看看最UVLED线光源。 下面以CCS的光…

某红书旋转滑块验证码分析与协议算法实现

文章目录 1. 写在前面2. 接口分析3. 验证轨迹4. 算法还原【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作! 【🌟作者推…

zoomeye api报错 request invalid, validate usage and try again

项目场景&#xff1a; 调用zoomeye的api接口进行数据拿取 问题描述 之前接口一直通着今天突然报错&#xff0c;以下为源代码 pip install zoomeye from zoomeye.sdk import ZoomEye zm ZoomEye(api_key"34A8B452-D874-C63E0-8471-F3D4f89766f") zm.dork_search(a…

图片像素缩放,支持个性化自定义与精准比例调整,让图像处理更轻松便捷!

图片已经成为我们生活中不可或缺的一部分。无论是社交媒体的分享&#xff0c;还是工作文档的编辑&#xff0c;图片都扮演着至关重要的角色。然而&#xff0c;你是否曾经遇到过这样的问题&#xff1a;一张高清大图在上传时却受限于平台的大小要求&#xff0c;或者一张小图需要放…

Spring MVC 源码分析之 DispatcherServlet#processDispatchResult方法

前言&#xff1a; 前面的篇章我们分析了 Spring MVC 工作流程中的 HandlerMapping、HandlerAdapter 的适配过程、拦截器的工作流程&#xff0c;以及处理业务请求的过程&#xff0c;本篇我们分析一下处理完业务解析视图的方法&#xff0c;也就是 DispatcherServlet#processDisp…

提高篇(二):高级绘图技巧:在Processing中创造精美图形

提高篇(二):高级绘图技巧:在Processing中创造精美图形 引言 Processing不仅是一种编程语言,更是一个用于创意编程的强大工具。掌握高级绘图技巧,可以让你在艺术创作中如虎添翼。在本篇文章中,我们将探索贝塞尔曲线、样条曲线、自定义形状、图形变换等高级绘图技术,帮助…

tkinter+火山引擎+python实现语音识别聊天机器人

想要做一款能通过语音识别来聊天的智能机器人,首先需要能通过麦克风录制语音进行识别转换成文字,将文字发送给机器人得到聊天结果,并能将返回的文字转换成语音进行合成,之后再通过本地播放语音实现语音交互。 架构: 实现步骤 一、本地录音 本地录音可以通过pyAudio库实…

2024-06-05-记一次cnvd渗透

前言&#xff1a;挖src挖郁闷了&#xff0c;闲来无事选择挖一个cnvd来练练手&#xff0c;本次的漏洞都没啥难度&#xff0c;企查查资产过了5000万 说一下cnvd证书的下放标准 对于中危及中危以上通用型漏洞&#xff08;CVSS2.0基准评分超过4.0分&#xff09;&#xff0c;以及涉…

Scaling vision Transformer 论文理解

Scaling vision Transformer 论文理解 1. 摘要2. 一些主要结论小结2.1 few shot transfer learning2.2 Pareto-front3. 讨论3.1 Limitations3.2 社会作用4. 文章结论参考资料1. 摘要 Attention-based neural networks such as the Vision Transformer (ViT) have recently att…

valgrind-内存泄漏定位工具

1、前言 valgirnd 是一套开放源代码的动态调试工具集合。能够检测内存管理错误&#xff0c;线程BUG等。valgirnd是由内核以及基于内核的其他调试工具组成。内核类似于一个框架&#xff0c;它模拟了一个cpu的环境&#xff0c;并提供服务给其他工具使用。而其他工具则类似于插件…

红酒:红酒保存中的光照与避免阳光直射

在红酒保存中&#xff0c;光照是一个常常被忽视的因素。光照对红酒的影响是不可小觑的&#xff0c;因为阳光中的紫外线会加速红酒的氧化&#xff0c;导致其口感和品质的下降。因此&#xff0c;在保存云仓酒庄雷盛红酒时&#xff0c;应特别注意避免阳光直射。 阳光直射对红酒的影…

企业代码签名证书1300元

随着手机和电脑等设备的普及&#xff0c;越来越多的开发者进入软件行业&#xff0c;为了软件的安全性、完整性和可信度&#xff0c;开发者往往会使用由正规CA认证机构颁发的代码签名证书对软件代码进行数字签名&#xff0c;来标识软件的来源和软件开发者的真实身份。今天就随SS…