当前位置: 首页 > news >正文

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 } })
}

日志打印:

功能已经正常!!!

相关文章:

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

需求 最近遇到个小程序异步解码的需求&#xff0c;采用了WebAssembly&#xff0c;涉及大量的计算。由于小程序的双线程模型只有一个线程处理数据&#xff0c;因此智能寻求其它的解决方案。查看小程序的文档&#xff0c;发现小程序还提供一个异步线程的Worker方案&#xff0c;可…...

C语言基础——数组(2)

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;C语言基础&#xff1b; 文章目录 前言 一、二维数组的创建 1.1 二维数组的概念 1.2二维数组的创建 二、二维数组…...

封装PHP用于发送GET和POST请求的公共方法

封装了ThinkPHP用于发送GET和POST请求的公共方法。这个方法可以放在你的公共函数文件中&#xff0c;或者创建一个独立的类来管理这些请求。 <?php namespace app\common\utils;use think\facade\Log; use think\exception\HttpException;class HttpRequest {/*** 发送GET请…...

MongoDB~基础知识记录

为何要学Mongodb 工作以来&#xff0c;使用最多、了解最多的是MySQL。但技术的发展一定是依据痛点来的&#xff0c;就比如我遇到的痛点&#xff0c;一个业务、一个平台能力、存储的一个对象&#xff0c;随着产品和运营的需求&#xff0c;不断的进行变更&#xff0c;每一次的变…...

DSP28335模块配置模板系列——ADC配置模板

一、配置步骤 1.使能并配置高速时钟HSPCLK、ADC校验 EALLOW;SysCtrlRegs.PCLKCR0.bit.ADCENCLK 1; EDIS;EALLOW;SysCtrlRegs.HISPCP.all ADC_MODCLK; // HSPCLK SYSCLKOUT/(2*ADC_MODCLK)ADC_cal();EDIS; 这里ADC_MODCLK3&#xff0c;所以HSPCLK时钟为150/625Mhz 2.配…...

字符串转换为字节数组、16进制转换为base64、base64转换为字符串数组、base64转换为16进制(微信小程序)

1、字符串转换为字节数组 // 字符串转为字节数组 function stringToByteArray(str) {var array new Uint8Array(str.length);for (var i 0; i < str.length; i) {array[i] str.charCodeAt(i);}return array; } 2、16进制转换为base64 // 16进制转换为base64 function H…...

c++中, 直接写浮点数, 是float 还是 double?

如果直接一个浮点数, 那么他默认是float还是double呢? 测试用例 #include <iostream> using namespace std;int main() {auto x 0.2;float f 0.2;double d 0.2;cout << "x Size : " << sizeof(x) << " bytes" << endl…...

C++核心编程友元的应用

文章目录 1.友元1.什么是友元2.全局函数做友元2.类做友元3.成员函数做友元 1.友元 1.什么是友元 在C中&#xff0c;友元&#xff08;friend&#xff09;是一种允许一个类或函数访问另一个类的非公有&#xff08;private 或 protected&#xff09;成员的机制。这种机制打破了类…...

C#,JavaScript实现浮点数格式化自动保留合适的小数位数

目标 由于浮点数有漂移问题&#xff0c;转成字符串时 3.6 有可能得到 3.6000000000001&#xff0c;总之很长的一串&#xff0c;通常需要截取&#xff0c;但按照固定长度截取不一定能使用各种情况&#xff0c;如果能根据数值大小保留有效位数就好了。 C#实现 我们可以在基础库里…...

Android基础-工程目录结构说明

Android工程的项目目录结构是开发Android应用时的基础&#xff0c;它组织和存储了应用的所有源代码、资源和配置文件。了解并熟悉这个目录结构对于提高开发效率和代码管理至关重要。下面将详细阐述Android工程的项目目录结构。 1. 工程根目录 Android工程的根目录通常包含多个…...

浅谈提示词发展现状,Prompt 自动优化是未来。

#封面手绘于本科期间&#xff0c;当年在知乎上写的第一篇关于 AI 的文章就用的这个封面&#xff0c;聊表纪念。 这次我们来聊聊 Prompt. 本来想取一个类似“提示词不存在了…”&#xff0c;或是“再见&#xff0c;Prompt 课程…”的标题&#xff0c;但最近很多大佬的谬赞让我感…...

揭秘智能测径仪省钱之道!每年能为每条产线省上百万!

在当今竞争激烈的市场环境下&#xff0c;企业们都在不断寻求提高生产效率、降低成本的方法。而智能测径仪的出现&#xff0c;为圆形钢材、螺纹钢等生产企业实现这一目标提供了有力的支持。 智能测径仪被广泛应用于高线、铸管、圆钢、螺纹钢、钢筋等的轧制生产线中&#xff0c;进…...

echaerts图例自动滚动并隐藏翻页按钮

效果图 代码 legend: {itemHeight: 14,itemWidth: 14,height: "300", //决定显示多少个// 通过 CSS 完全隐藏翻页按钮pageButtonItemGap: 0,pageButtonPosition: end,pageIconColor: transparent, // 隐藏翻页按钮pageIconInactiveColor: transparent, // 隐藏翻页按…...

OpenCV的小部件最基本范例

OpenCV也有与PYQT类似的小部件&#xff0c;例如滑块slider。OpenCV可以用与PYQT类似的“信号与槽”方法&#xff0c;也可以在函数中直接查询小部件的值。 import cv2 import numpy as npcv2.namedWindow(Show1) image np.zeros((100, 400, 3), np.uint8) # 创建一个空白内容…...

内置类型知多少?

内置类型&#xff08;也称为基本类型或原生类型&#xff09;是C/C本身定义的数据类型&#xff0c;它们直接由编译器支持&#xff0c;不需要用户自定义。 内置类型主要包括以下几类&#xff1a; 1&#xff0e;算术类型&#xff1a; (1)整型&#xff1a;int、short、long、lon…...

【C++题解】1090 - 同因查找

问题&#xff1a;1090 - 同因查找 类型&#xff1a;for循环、简单循环 题目描述&#xff1a; 求出 10 至 1000 之内能同时被2、3、7 整除的数&#xff0c;并输出。 每行一个。 输入&#xff1a; 无。 输出&#xff1a; 按要求输出满足条件的数&#xff0c;每行 1 个。 完…...

uni微信小程序editor富文本组件如何插入图片

需求 在editor中插入图片&#xff0c;并对图片进行编辑&#xff0c;简略看一下组件的属性&#xff0c;官网editor 组件 | uni-app官网 解决方案 首先要使用到ready这个属性&#xff0c;然后官网有给代码粘过来&#xff0c;简单解释一下这段代码的意思&#xff08;作用是在不同…...

LabVIEW调用国产硬件DLL的稳定性问题及解决方案

在LabVIEW中调用国内公司提供的硬件DLL时&#xff0c;尽管可以运行&#xff0c;但常出现不稳定和bug问题&#xff0c;且厂家临时修改的版本未经长期测试。为确保稳定性和质量&#xff0c;需要制定系统化的测试和反馈机制、建立严格的版本控制、与厂家协作优化、并进行深入的自测…...

基础篇01——SQL的基本语法和分类

MySQL数据库安装与基本使用 安装教程参见&#xff1a;通过zip安装MySQL 通过命令行启动和停止MySQL服务命令 前提&#xff1a;安装MySQL成功之后 启动服务&#xff1a;net start mysql 停止服务&#xff1a;net stop mysql 通过命令行连接mysql 可以通过mysql的客户端命令行…...

HOW - 面试技巧系列 - 全英文面试

自我介绍 “can you tell me a little bit about yourself?” “please introduce yourself.” 工作经验 “can you describe your most recent job experience?” “how does your experience make you a good fit for this position?” 职业规划 “what are your l…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...