如何避免Node线程工作器导致Electron Vite应用冻结?
Electron Vite中Web Worker文件导入导致主进程阻塞问题
我正在Electron Vite应用中尝试用Web Worker实现文件导入,目的是避免阻塞主进程。按官方文档操作后,出现以下问题:
- 调用Worker时应用出现视觉冻结,文件复制能正常完成,但有时结束后需要等待一段时间才能恢复,甚至要重启应用
- 尝试文档中第二种Worker使用方式时,应用无法启动,报错提示
importWorker无默认导出,和使用子进程时的错误一致
相关代码位于仓库的workers分支。
主进程入口文件 main/index.ts
import { importFiles } from "./import"; // Other imports function createWindow(): void { // Create the browser window. const mainWindow = new BrowserWindow({ width: 1400, height: 900, minHeight: 600, minWidth: 1100, show: false, autoHideMenuBar: false, ...(process.platform === "linux" ? { icon } : {}), webPreferences: { preload: path.join(__dirname, "../preload/index.js"), sandbox: false, nodeIntegrationInWorker: true, }, }); [...] apiHandling(); [...] }); [...] const apiHandling = () => { // more communication handling ipcMain.on("import-files", (_, data) => { importFiles(data); }); }; [...]
导入逻辑文件 main/import.ts
import { ipcMain } from "electron"; import { Worker } from "worker_threads"; import importWorker from "./workers/importWorker?nodeWorker"; const runningImports = new Map<string, { data: ImportData; worker: Worker }>(); // 该方法用于启动导入(复制到库文件夹),设计上每次调用都会创建新Worker,在后台完成复制并通过ipcMain.emit()向渲染进程发送进度 export const importFiles = (data: ImportData) => { if (runningImports.has(data.id)) return; const worker = importWorker({ workerData: data, }); runningImports.set(data.id, { data, worker }); worker.on("message", (message: ImportWorkerMessage) => { const { type, ...data } = message; ipcMain.emit("import-message", type, data); if (type === "done") { worker.terminate(); } }); worker.on("error", (err) => { console.error(err); }); worker.on("exit", (code) => { runningImports.delete(data.id); }); };
Worker文件 main/workers/importWorker.ts
// Worker逻辑 import fs from "fs"; import p from "path"; import { parentPort, workerData } from "worker_threads"; if (!parentPort) { throw new Error("No parentPort found"); } const { paths, options, id } = workerData as ImportData; const fileQueue = [...paths]; const processFiles = () => { if (fileQueue.length === 0) { parentPort!.postMessage({ type: "done", id }); return; } // 目前将paths数组中的每个文件复制到桌面 const path = fileQueue.shift()!; const fileName = p.basename(path); const readStream = fs.createReadStream(path); const writeStream = fs.createWriteStream( p.join("C:\\Users\\filip\\Desktop", fileName) ); parentPort!.postMessage({ type: "fileStart", id, fileName, }); readStream.on("data", (chunk) => { parentPort!.postMessage({ type: "progress", id, fileName, chunkLength: chunk.length, }); }); readStream.on("end", () => { parentPort!.postMessage({ type: "fileDone", id, fileName, }); processFiles(); }); readStream.pipe(writeStream); writeStream.on("error", (error) => { parentPort!.postMessage({ type: "error", id, fileName, error, }); }); }; processFiles();
Preload文件 preload/index.ts
import: { importFiles: (path: string[], options: unknown) => { ipcRenderer.sendSync("import-files", path, options); }, onMessage: (callback: (event: unknown, data: unknown) => void) => { ipcRenderer.addListener("import-message", callback); return () => ipcRenderer.removeListener("import-message", callback); }, },
内容的提问来源于stack exchange,提问作者UnderMan4
相关产品推荐
相关产品推荐

