This article was first published on the public account [an old code farmer]

Recently participated in a company’s desktop application due to business needs. The technology stack is electron+ React, and THE interface part is mainly antD.

During the development process, I encountered a problem: THERE was a time-consuming task. When the task was started, the entire application interface froze and the interface operation could only be performed after the task was completed. Today we will talk about the processing method of intensive tasks in electron.

Caton reason

Since electron uses JS as its development language, and JS is single-threaded, electron does not have multithreading, but it does allow multiple processes. Electron is divided into the main process and the renderer process. The main process is controlled in the main file, which is also responsible for controlling the application life cycle. (Note: The main file is configured in the main field of the package.json configuration file.) And each window is a renderer called the UI process. According to the research, the application interface is stuck because the task is expensive, which causes the current rendering process to be stuck.

How to avoid UI stutter

The electron documentation gives the following tips on how to optimize the electron application: Whenever a core node.js module such as FS provides a synchronous or asynchronous version, you should use asynchronous and non-blocking variables

If possible, avoid using the synchronous IPC and Remote module, which can be very easy to block UI threads unwittingly

For heavy tasks that need to hog CPU for a long time, you can move them to BrowserWindow and generate a dedicated process. (Create a new window)

Specific Solutions

Based on the official documentation, I used a new hidden BrowserWindow to put time-consuming tasks in the new window. Since each BrowserWindow is a separate rendering process, this method avoids interface stutters. The progress of the task processing, the hidden renderer process (replaced by “task process”) can be told to the main process through IPC communication, and the main process will inform the current renderer process. For convenience, remote.ipcmain.on () can also be used in the current renderer to communicate with the task process.

The core code is as follows:

Listen in the current renderer process, because the renderer process and the renderer process cannot communicate, so you can use remote. IpcMain to get the main process object to listen

Import {remote} from 'electron' import React, {useState, useEffect, useRef } from 'react' const win = useRef() useEffect(() => { return () => { closeArchiveRenderer() } }, Function begain() {listenIpcRenderer() createNewWindow()} function begain() {listenIpcRenderer() createNewWindow()} function createNewWindow() {let win.current = new remote.BrowserWindow({ width: 500, height: 500, show: false, webPreferences: { nodeIntegration: true, contextIsolation: false, enableRemoteModule: true, }, }) win.current. LoadURL (' XXXXXX ')} function listenIpcRenderer() {remote.ipcmain. on('window-load-success', (event, arg) => {console.log(arg) if (arg) {event.reply(' began-task ', {//tag Indicates the task type tag: 'XXX ', // dataSource to task process: dataSource }) } }) remote.ipcMain.on('change-task-status', (event, Arg) => {console.log(arg) if (arg.error) {console.log(arg.error) return} if (arg.status === 2) {// Status processing}}) Remote. Ipcmain. on('task-complete', (event, arg) => { CloseArchiveRenderer ()})} // To close the process at the right time, Function closeArchiveRenderer() {if (win.current) {win.current-close () win.current = null} function closeArchiveRenderer() {if (win.current) {win.current-close () win.current = null} remote.ipcMain.removeAllListeners() }Copy the code

This is required in task-specific processes

// Task process, Import {ipcRenderer} from 'electron' useEffect(() => {ipcrenderer. on('begain-task', (event, Arg) => {if (arg. Tag === 'XXXX ') {console.log(arg. DataSource) // TODO XXXX () // The following code can be placed in the appropriate position as required Ipcrenderer. send('change-task-status', data) // If the task is completed, Ipcrenderer. send('task-complete', data)}}) ipcrenderer. send('window-load-success', true) return () => { ipcRenderer.removeAllListeners() } }, [])Copy the code

Pay attention to the public number [an old code farmer], more high-quality technical content waiting for you

The original address