西安建站平台哪个好/东莞网络推广及优化
需求
最近遇到个小程序异步解码的需求,采用了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 开发模板https://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】
需求 最近遇到个小程序异步解码的需求,采用了WebAssembly,涉及大量的计算。由于小程序的双线程模型只有一个线程处理数据,因此智能寻求其它的解决方案。查看小程序的文档,发现小程序还提供一个异步线程的Worker方案,可…...

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

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

MongoDB~基础知识记录
为何要学Mongodb 工作以来,使用最多、了解最多的是MySQL。但技术的发展一定是依据痛点来的,就比如我遇到的痛点,一个业务、一个平台能力、存储的一个对象,随着产品和运营的需求,不断的进行变更,每一次的变…...
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,所以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中,友元(friend)是一种允许一个类或函数访问另一个类的非公有(private 或 protected)成员的机制。这种机制打破了类…...

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

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

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

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

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

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

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

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

uni微信小程序editor富文本组件如何插入图片
需求 在editor中插入图片,并对图片进行编辑,简略看一下组件的属性,官网editor 组件 | uni-app官网 解决方案 首先要使用到ready这个属性,然后官网有给代码粘过来,简单解释一下这段代码的意思(作用是在不同…...

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

基础篇01——SQL的基本语法和分类
MySQL数据库安装与基本使用 安装教程参见:通过zip安装MySQL 通过命令行启动和停止MySQL服务命令 前提:安装MySQL成功之后 启动服务:net start mysql 停止服务: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…...

【人工智能】第六部分:ChatGPT的进一步发展和研究方向
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...

新能源管理系统主要包括哪些方面的功能?
随着全球对可持续发展和环境保护的日益重视,新能源管理系统已成为现代能源领域的核心组成部分。这一系统不仅涉及对新能源的收集、存储和管理,还包括对整个能源网络进行高效、智能的监控和控制。以下是新能源管理系统主要包含的几方面功能: 一…...

python如何快速的判断一个key在json的第几层呢,并修改其value值
python如何快速的判断一个key在json的第几层呢,并修改其value值 def find_and_modify_key(json_obj, target_key, new_value, current_level1):# 检查当前层是否包含目标keyif target_key in json_obj:print(f"找到 {target_key} 在第 {current_level} 层。&q…...

Python基础用语:揭示编程世界的底层逻辑与无限可能
Python基础用语:揭示编程世界的底层逻辑与无限可能 Python,这门强大的编程语言,如同一位神秘的导师,引领我们走进编程的奇妙世界。掌握Python的基础用语,不仅意味着我们能够驾驭这门语言,更意味着我们能够…...

react 中使用 swiper
最近项目中需要用到轮播图,我立马想起了 swiper ,那么本文就来带大家体验一下如何在 React 中使用这个插件,使用的是 函数组 hooks 的形式。 需求非常简单,就是一个可以自动播放、点击切换的轮播图(跑马灯࿰…...

2024年搭建知识库系统5大工具推荐
现如今,越来越多的企业开始了解并搭建知识库系统,想要带动企业员工的工作效率以提高企业运营效率。各种各样搭建知识库系统的工具应运而生,而如何在众多工具中,选择出最适合独特的企业产品的一个工具,成为企业亟待解决…...

Golang内存分配分三种Golang内存分配分三种类型
Golang内存分配分三种类型 线程缓存(Thread Cache) 拟人化解释:线程缓存就像是每个邮局员工的私人储物柜。每个员工都有自己的储物柜,用来暂时存放需要处理的小信件和包裹。当有新的信件或包裹需要处理时,员工会先看看…...

Marin说PCB之如何在主板上补偿链路中的走线的等长误差?
一场雨把我困在这里,你冷漠地看我没有穿雨衣淋成落汤鸡。今天刚刚出门时候看天气预报没有雨,于是我就没有带雨衣骑电动车去公司了,谁知道回来的路上被淋成狗了。天气预报就像是女人的脾气那样,不能完全相信的。 好了,我…...

企业在现代市场中的战略:通过数据可视化提升财务决策
新时代,财务规划团队不仅仅是企业内部的一个部门,更是帮助企业做出明智决策和设定战略目标的中坚力量。在当今瞬息万变的商业环境中,财务专业人士需要具备应对挑战并引导企业走向成功的角色职能。企业领导者时常面临着数据压力,需…...

printf(“不喝酒就没得朋友可是只要偶一喝酒就喝倒一代朋友人生真的很矛盾“);
/*无线通讯语言模块测试PAST 2019 12 28 L298 CODE1871**/ #include <REG52.H> #include <intrins.H> #include "stdio.h" #define uint unsigned int #defi…...