Shiv ENOW large front end

Company official website: CVTE(Guangzhou Shiyuan Stock)

Team: ENOW team of CVTE software Platform Center for Future Education

The author:

preface

You wish the world, I wish you no bugs. Hello everyone! I’m Lin Dull.

It seems so long since the last time we met… It must have been last year…

I would like to apologize for the delay of the article, but I will make it up gradually this year (╹, ╹). (But again, you’ll forgive me for being so pretty.)

OKK, talk about the article. Since the recent work content is mainly desktop development, there will be more articles related to electron. This series is mainly about the IPC module in ELECTRON. We can rest assured that the quality and content of the article will be easy to understand, and combined with the actual case to talk about each piece of knowledge, and then summarize each part.

But as it is a series of articles, there will certainly be a lot of relatively basic content, big guy please skip, if it is a electron beginner, I believe you will gain something (* ̄)  ̄).

This series consists of the following chapters:

  • The main process and the renderer process love each other
  • The renderer connects to the renderer process
  • A telephonist of loveport

This time you are reading chapter 1: The love affair between the main process and the renderer process.

Note: All of the above articles are filed to: github.com/LinDaiDai/n… , cases are uploaded to: github.com/LinDaiDai/e… Welcome Start, thank Start.

Case Version Information:

  • Electron: v13.6.7
  • Nodejs: v16.13.2

The outline

When it comes to the communication in the electron process, we have to talk about the officially provided IPC module. This mode of communication mainly relies on the ipcMain and ipcRenderer modules provided by Electron.

The communication is divided into the following parts:

  • The renderer sends synchronous/asynchronous messages to the main process
  • The main process sends synchronous/asynchronous messages to the renderer process

1. The renderer sends synchronous/asynchronous messages to the main process

The renderer relies on the ipcRenderer module to send messages to the main process.

  • ipcRenderer.send(channel, ... args)
  • ipcRenderer.invoke(channel, ... args)
  • ipcRenderer.sendSync(channel, ... args)

Channel represents the event name (message name), and args is easy to understand, which is the parameter.

These three methods can send a message to the main process, but also can wait for the main process promise, really so-called is two love mutually yue, mutual call, but the way of promise is different.

1.1 IpcRender. Send case

Render. Js:

// render.js
const { ipcRenderer } = require('electron');

function sendMessageToMain() {
  ipcRenderer.send('render-send-to-main'.'I'm a message sent by the renderer process');
}
Copy the code

Main process main.js:

// main.js
const { ipcMain } = require('electron');

ipcMain.on('render-send-to-main'.(event, message) = > {
  console.log(`receive message from render: ${message}`)})Copy the code

1. The main process listens for messages from the renderer process via ipcmain. on;

2. After receiving a message, the main process can reply to the message or not. If there is a reply, another event is sent via event.reply and the renderer listens for the reply. If the message is not returned, the renderer will continue to execute the code after ipcrenderer.send.

As mentioned above, the main process can send a reply or not, but only through event.replay. If you try to return the value, the result will not be as good as you expected.

// render.js
const { ipcRenderer } = require('electron');

function sendMessageToMain() {
  const replyMessage = ipcRenderer.send('render-send-to-main'.'I'm a message sent by the renderer process');
  console.log(replayMessage); // undefined
}

// main.js
const { ipcMain } = require('electron');

ipcMain.on('render-send-to-main'.(event, message) = > {
  console.log(`receive message from render: ${message}`)
  return 'The main process is trying to pass a message to the renderer';
})
Copy the code

The return value of send is undefined, even if an attempt is made to return the value in ipcmain. on.

The correct way to do this is to send another event via event.reply, and the renderer listens for the result:

Render. Js:

// render.js
const { ipcRenderer } = require('electron');

// The send method sends and binds another event to receive the return value
function sendMessageToMain() {
  ipcRenderer.send('render-send-to-main'.'I'm a message sent by the renderer process');
}
ipcRenderer.on('main-reply-to-render'.(event, message) = > {
  console.log('replyMessage', message); // 'replyMessage the main process replies to the renderer process via reply '
})
Copy the code

Main process main.js:

// main.js
const { ipcMain } = require('electron');

ipcMain.on('render-send-to-main'.(event, message) = > {
  console.log(`receive message from render: ${message}`)
  event.reply('main-reply-to-render'.'The main process replies to the renderer via Reply')})Copy the code

1.2 IpcRender. Invoke case

Render. Js:

// render.js
const { ipcRenderer } = require('electron');

async function invokeMessageToMain() {
  const replyMessage = await ipcRenderer.invoke('render-invoke-to-main'.'I am a message sent by the renderer process via Invoke');
  console.log('replyMessage', replyMessage);
}
Copy the code

Main process main.js:

// main.js
const { ipcMain } = require('electron');

ipcMain.handle('render-invoke-to-main'.async (event, message) => {
  console.log(`receive message from render: ${message}`)
  const result = await asyncWork();
  return result;
})

const asyncWork = async() = > {return new Promise(resolve= > {
    setTimeout(() = > {
      resolve('2 seconds delay in getting results from main process')},2000)})}Copy the code

1. The main process processes the messages sent by the renderer process through ipcmain. handle;

2. After receiving a message, the main process can reply to the message or not. If a message is returned, you can return it to the renderer. If the message is not returned, the renderer will proceed to execute the code after IPCrenderer.invoke.

3. The renderer waits asynchronously for a response from the main process, invoke returns a Promise .

To verify the third point, we can receive the return result of invoke without await and see what it looks like:

// render.js
const { ipcRenderer } = require('electron');

function invokeMessageToMain() {
  const replyMessage = ipcRenderer.invoke('render-invoke-to-main'.'I am a message sent by the renderer process via Invoke');
  console.log('replyMessage', replyMessage); // Promise<pending>
}
Copy the code

The printed result is as expected, a Promise .

Ipc.Main is a function that executes asynchronously. Ok, let’s change main.js as well:

// render.js
const { ipcRenderer } = require('electron');

function invokeMessageToMain() {
  const replyMessage = ipcRenderer.invoke('render-invoke-to-main'.'I am a message sent by the renderer process via Invoke');
  console.log('replyMessage', replyMessage); // Promise<pending>
}

// main.js
ipcMain.handle('render-invoke-to-main'.(event, message) = > {
  console.log(`receive message from render: ${message}`)
  const result = 'I am the result of the synchronization of the main process.';
  return result;
})
Copy the code

We changed the return value of ipcmain. handle to a normal string and ipcRender. Invoke returned a Promise . This also verifies our conclusion above.

1.3 IpcRender sendSync case

Render. Js:

// render.js
const { ipcRenderer } = require('electron');

function sendSyncMessageToMain() {
  const replyMessage = ipcRenderer.sendSync('render-send-sync-to-main'.'I'm a message that the renderer sends to the main process via syncSend');
  console.log('replyMessage', replyMessage); // 'Message returned by main process'
}
Copy the code

Main process main.js:

// main.js
const { ipcMain } = require('electron');

ipcMain.on('render-send-sync-to-main'.(event, message) = > {
  console.log(`receive message from render: ${message}`)
  event.returnValue = 'Message returned by main process';
})
Copy the code

1. The main process processes the messages sent by the renderer process via ipcmain. on;

2. The main process returns the renderer message via event.returnValue;

3. If event.returnValue is not undefined, the renderer will wait for sendSync to return the value before executing the following code.

4. Ensure that event. ReturnValue is valid; otherwise, unexpected effects will be caused.

The first two points are easy to understand, and the third point is a little bit convoluted, so let’s write two more examples here to help you understand.

In the above example, the main process is bound to a synchronous handler, so let’s change it to an asynchronous handler:

// main.js
const { ipcMain } = require('electron');

ipcMain.on('render-send-sync-to-main'.async (event, message) => {
  console.log(`receive message from render: ${message}`)
  const result = await asyncWork();
  event.returnValue = result;
})

const asyncWork = async() = > {return new Promise(resolve= > {
    setTimeout(() = > {
      resolve('2 seconds delay in getting results from main process')},2000)})}Copy the code

This time we assign event. ReturnValue after executing an asynchronous function asyncWork.

It turns out that the renderer will print after 2 seconds:

"ReplyMessage delay of 2 seconds to get return result from main process"Copy the code

And for the rendering process, the result is the same either way:

// render.js
const { ipcRenderer } = require('electron');

function sendSyncMessageToMain() {
  const replyMessage = ipcRenderer.sendSync('render-send-sync-to-main'.'I'm a message that the renderer sends to the main process via syncSend');
  console.log('replyMessage', replyMessage); // 'replyMessage delay 2 seconds to get return result from main process '
}

// Or use async instead
async function sendSyncMessageToMain() {
  const replyMessage = await ipcRenderer.sendSync('render-send-sync-to-main'.'I am a synchronization message sent by the renderer process to the main process');
  console.log('replyMessage', replyMessage); // 'replyMessage delay 2 seconds to get return result from main process '
}
Copy the code

That is, whether or not the renderer waits with await when it receives sendSync results, it will wait for the result to return before proceeding. However, if you are sure that your request is asynchronous, it is recommended to use Invoke to send a message for two reasons:

The method name sendSync is semantic enough to send synchronous messages.

2. It would be strange to use const replyMessage = ipcrenderer.sendsync (‘ XXX ‘) to retrieve the response when the request is clearly asynchronous code.

OKK, as mentioned in # 4 above, make sure event. ReturnValue has a value, otherwise it will have an unintended effect. Let’s also write an example:

// render.js
const { ipcRenderer } = require('electron');

function sendSyncMessageToMain() {
  const replyMessage = ipcRenderer.sendSync('render-send-sync-to-main'.'I'm a message that the renderer sends to the main process via syncSend');
  console.log('replyMessage', replyMessage); // replyMessage {error: "reply was never sent"}
  console.log('next'); // This will also be executed here
}

// main.js
ipcMain.on('render-send-sync-to-main'.async (event, message) => {
  console.log(`receive message from render: ${message}`)})Copy the code

In the example above, the main process does not handle the event. ReturnValue, and the renderer will get an error:

{error: "reply was never sent"}
Copy the code

Next will also print, but if you try to send render-send-sync-to-main again you’ll find that the page is already stuck…

Ok, so far as I have read the article, I am almost there. The way in which the main process sends messages to the renderer is already described in the introduction of the renderer sending messages, but let’s take a look at it.

2. The main process sends synchronous/asynchronous messages to the renderer process

The corresponding ipcRenderer module is ipcMain. The main process relies on the ipcMain module to send messages to the renderer in the following ways:

  • event.reply: The main process passesonListen for messages if used by the renderersendMessages can be sent in theonGets the event object from the callback function and passesevent.replySend another event;
  • return: The main process passeshandleListen for processing messages if used by the renderer processinvokeMessages can be sent in theonIs passed in the callback functionreturnReply to messages;
  • event.returnValue: The main process passesonListen for messages if used by the renderersendSyncMessages can be sent in theonThe callback function is set toevent.returnValueReply to messages;
  • window.webContents.send: of the window instance passed by the main processwebContentsSends a message to the renderer process within this window.

The previous three types of renderer sending messages have been covered, so let’s look at window.webContents.

2.1 Window. WebContents. Send case :

This approach relies on the webContents object, which is a property on the window object generated when we create a new window in the project.

For example, we send a message when a window has finished loading:

// main.js
const { app, BrowserWindow } = require('electron')

function createWindow() {
  const window = new BrowserWindow({
    width: 800.height: 600.webPreferences: {
      nodeIntegration: true.contextIsolation: false}})window.loadFile('src/index.html')

  // When the window finishes loading, it sends a message to the renderer via webcontents.send
  window.webContents.on('did-finish-load'.() = > {
    window.webContents.send('main-send-to-render'.'Startup complete')
  })
}

app.whenReady().then(createWindow)
Copy the code

Render. Js:

// render.js
const { ipcRenderer } = require('electron');

ipcRenderer.on('main-send-to-render'.(event, message) = > {
  console.log(`receive message from main: ${message}`)})Copy the code

Through the above operations, the main process can also achieve the effect of sending messages to the rendering process (* ̄).

3. Summary

OKK, having introduced the features and analyzed the case, let’s summarize.

  • If throughipcMainipcRenderer, the renderer can send messages to the main process in three ways:
    • ipcRenderer.send(channel, ... args)
    • ipcRenderer.invoke(channel, ... args)
    • ipcRenderer.sendSync(channel, ... args)
  • Through the above three ways, the main process can have the corresponding method to give the rendering process, but the method is different;
  • Synchronizing requests is recommendedsendSync, recommended for asynchronous requestsinvoke;
  • The main process can also get a rendererwindow.webContentsAnd then throughwindow.webContents.sendSend a message to the renderer process;

Tips:

Cases are uploaded to: github.com/LinDaiDai/e… Welcome Start, thank Start.

After the language

So much for this article. It can be found that the knowledge points to explain in this article are actually quite basic, but when I was just learning, I did not find that there was a particularly good teaching material to explain, so this is not “diy, abundant food and clothing” the whole article?

Ha ha ha, but rest assured, this series will continue, after all, there is a lot more to cover, let’s look forward to the next article: Rendering process and rendering process matchmaking.

Like “Lin dull” guy also hope to pay attention to Lin dull public number LinDaiDai.

I will update some front-end knowledge content and my original article 🎉 from time to time

Your encouragement is the main motivation for my continuous creation 😊.