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.pswp.cn/web/23188.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/23188.shtml
英文地址,請注明出處:http://en.pswp.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…