fs.readdir inside ipcMain.handle does not return

1.7k Views Asked by At

it always returns undefined, any idea please? i need the return inside fs.reddir but relative to ipcMain

//main

ipcMain.handle('pegaDirRomSalvo', async (event, argx=0) => {
  fs.readFile('dataCR.txt', 'utf8', function (err,data) {

    if (err) {return '0'}
    fs.readdir(data, (err, files) => {
          if (err) {throw err;}
          return [data,files] //<-------- not going
    })
   
  })
})


//render

ipcRenderer.invoke('pegaDirRomSalvo',a=0).then((result)=>{
         document.getElementById('divCaminho').innerText = result[0]
})
3

There are 3 best solutions below

0
On BEST ANSWER

You need to use event.sender.send to trigger another call from the main to the renderer process to return the result, where the last will listen to it using ipcRenderer.on. Here is a sample solution:

renderer:

const { ipcRenderer } = require('electron');
ipcRenderer.invoke('pegaDirRomSalvo', a=0);
ipcRenderer.on('FILES_LIST_FETCHED', (event, result) => {
     console.log(result);
     document.getElementById('divCaminho').innerText = result[0]
});

main:

const { ipcMain } = require('electron');
ipcMain.handle('pegaDirRomSalvo', async (event, argx=0) => {
     fs.readFile('dataCR.txt', 'utf8', function (err,data) {
          if (err) { return '0'; }
          fs.readdir(data, (err, files) => {
               if (err) {throw err;}
               event.sender.send('FILES_LIST_FETCHED', [data, files]);
          })
     });
});

You can do the same for returning the error details if you want.

EDIT: This technique is usually used when sending the renderer call using ipcRenderer.send and receiving it on the main process using ipcMain.on.

0
On

From the ipcMain.handle(channel, listener) docs:

If listener returns a Promise, the eventual result of the promise will be returned as a reply to the remote caller. Otherwise, the return value of the listener will be used as the value of the reply.

// main
ipcMain.handle("pegaDirRomSalvo", (event, argx = 0) => {
  return new Promise((resolve, reject) => {
    fs.readFile("dataCR.txt", "utf8", function (err, data) {
      if (err) {
        reject(err);
        return;
      }
      fs.readdir(data, (err, files) => {
        if (err) {
          reject(err);
          return;
        }
        resolve([data, files]);
      });
    });
  });
});

// render
ipcRenderer.invoke("pegaDirRomSalvo", (a = 0)).then((result) => {
  document.getElementById("divCaminho").innerText = result[0];
});

The code can be refactored using async/await and fs.promises to be much more readable

// main
const fsPromisified = require("fs/promises");

ipcMain.handle("pegaDirRomSalvo", async () => {
  const data = await fsPromisified.readFile("dataCR.txt", "utf-8");
  const files = await fsPromisified.readdir(data);
  return [data, files];
});

// render
(async () => {
  try {
    const result = await ipcRenderer.invoke("pegaDirRomSalvo");
    document.getElementById("divCaminho").innerText = result[0];
  } catch (err) {
    // handle errors
  }
})();

Or you can handle errors in the main process

0
On

for anyone looking at this in the future:

Was just having the same issue, and i don't have "access" to ipcRenderer in my renderer scripts. However, using a preload script, the key is to return the "promised result" from your ipcRenderer.invoke in your contextBridge.exposeInMainWorld api:

// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("renderer", {
  pegaDirRomSalvo: async (argx) => { 
    return ipcRenderer.invoke('pegaDirRomSalvo',a=0)
      .then((result)=>{
        return result;
      }
    )
  }
});

now you can create the function in the main file

//main.js
ipcMain.handle('pegaDirRomSalvo', async (event, argx=0) => {
  fs.readFile('dataCR.txt', 'utf8', function (err,data) {
    if (err) {return '0'}
    fs.readdir(data, (err, files) => {
      if (err) {throw err;}
      return [data,files]
    })
  })
})

and calling this in renderer (i.e., const val = window.renderer.pegaDirRomSalvo(1)) will (well, SHOULD) return the contents of [data,files]