How to handle 2 ipcRenderer.send calls to 1 ipcMain.on


#1

Here is the situation.

The database is directly access with nodejs (node-adodb) in main.js. My Angular app send the request and receive the sql result from the db.service. This part is working just fine, but if I have a function like getInfo(), the first returned promise would be the value of the second request. How would you suggest me to handle that situation to avoid that conflicts?

Yes, I could wait for the first call to return and then do the next one, but do you think it could be done differently?

Thanks in advance

Here is the code

main.js

    ipcMain.on('item:select', (event, sql) => {
      connection
        .query(sql)
        .on('done', function (data) {
          event.sender.send('async-reply', data);
        })
        .on('fail', function (error) {
          event.sender.send("async-reply", error);
        });
    });

db.service.ts

  select(sql: string): Promise<any> {
    return new Promise((resolve, reject) => {
      ipcRenderer.send('item:select', sql);

      ipcRenderer.on('async-reply', (event, result) => {
        resolve(result);
      });
    });

user.ts

  getInfo() {
    this.userService
      .getUsers()
      .then((value: User[]) => this.users = value);

    this.userService
      .getUserTypes()
      .then((value: UserType[]) => this.userType = value);
  }

#2

Hey,
I know you posted this a month ago, but a while back I ran into pretty much the same issue you were having. I came up with a solution using IPC calls, but I believe that you could also use pubsub, which has a event.resolve() mechanism that you can use to resolve specific instances of a publish event.

We had issues with using remote (which is needed for pubsub) so we had to use an IPC solution. What we ended up doing was when we tried to call a function in our model class, we would generate a unique call id using the random-uuid-v4 npm module. We then register a one time listener on the client side for the callback that includes the class name, the function name, and the call id, so it turned out something like MODEL.[func name]/[call id]. The call id would then be sent along with what function we were trying to use and any inputs through IPC, so something like ipcRenderer.send('MODEL', [call id], [func name]) The main process would then send on the channel MODEL.[func name]/[call id] which will be unique per call instead of just some channel like MODEL or even MODEL.[func name]. This way, each call is listening on it’s own channel and nothing will get confused.

To recap,

Renderer Side:

// in call to Model.select
var callId = require('random-uuid-v4');
ipcRenderer.once('MODEL.select/' + callId, () => {
    // Handle result
});
ipcRenderer.send('MODEL', callId, 'select'); // Send request

Main Side:

// You can add ...args after func to get an arraylike named args
// containing any remaining parameters sent to use as arguments
// to your function call over here.
ipcMain.on('MODEL', (event, callId, func) => {
    // Retrieve result
    var result = 'result!'
    // Send response (the null is for any error, like a standard callback)
    event.sender.send('MODEL.' + func + '/' + callId, null, result);
});

Hope this helps!


#3

Also, you should register your listeners for things like this before actually sending the request, because otherwise you have a race condition and your listener might not even be called if the main process tries to send back before your listener can finish registering.


#4

Big thanks for your comment. It gave me the opportunity to do a great refactoring!