1

I'm trying to launch an exe file from an Electron app with React/Redux.

From the component i'm doing dispatch(launch(titleId, titleName))

The problem is i'm getting path undefined when i'm waiting for readFolders() async.

Any idea what i'm doing wrong and what should i change in my aproach?

Thanks in advance!

launch.js

export const launch = async (titleId, titleName) => {
  const path = await readFolders(titleId);
  console.log('path:', path) //undefined

  execFile(path, (err, data) => {
    if (err) {
      console.log('err', err);
    } else if (data) {
      console.log('data:', data)
    } else {
      console.log('success');
    }
  });

  return {
    type: 'LAUNCH',
  };
};

readFolders.js

import fs from 'fs';
import { homedir } from 'os';

const fsPromises = fs.promises;

const isExeFile = file => file.match(/.*\.exe$/i);

export const readFolders = async titleId => {
  const userDir = homedir();
  const folderPath = `${userDir}/downloads`;
  const fullPath = `${folderPath}/${titleId}`;

  try {
    const contents = await fsPromises.readdir(fullPath);
    contents.forEach(async item => {
      if (isExeFile(item)) {
        console.log('isExeFile');
        return `${fullPath}/${item}`;
      }
      try {
        const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
        nestedFolder.forEach(nestedItem => {
          if (isExeFile(nestedItem)) {
            return `${fullPath}/${item}/${nestedItem}`;
          }
          return null;
        });
      } catch (err) {
        console.log('err:', err);
      }
    });
  } catch (err) {
    console.log('err main:', err);
  }
};

Edit:

I also tried this way and now const path = await readFolders(titleId); returns the correct result, but this way eslint is complaining (https://eslint.org/docs/rules/no-async-promise-executor) and it doesn't feel like a good solution.

  return new Promise(async (resolve, reject) => {
    try {
      const contents = await fsPromises.readdir(fullPath);
      contents.forEach(async item => {
        if (isExeFile(item)) {
          console.log(`${fullPath}/${item}`);
          return resolve(`${fullPath}/${item}`);
        }
        try {
          const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
          nestedFolder.forEach(nestedItem => {
            if (isExeFile(nestedItem)) {
              console.log(`${fullPath}/${item}/${nestedItem}`);
              return resolve(`${fullPath}/${item}/${nestedItem}`);
            }
            return null;
          });
        } catch (err) {
          console.log('err:', err);
          reject(err);
        }
      });
    } catch (err) {
      console.log('err main:', err);
      reject(err);
    }
  });
Cristian Muscalu
  • 9,007
  • 12
  • 44
  • 76
  • 1
    [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572), and [don't use `forEach`](https://stackoverflow.com/a/37576787/1048572)! – Bergi Apr 07 '20 at 17:10

1 Answers1

0

Missing return at the end. When you return in forEach, It returns from callback anonymous function only. return ${fullPath}/${item}/${nestedItem};

For more you can read my blog on it: https://medium.com/@deepak_v/weird-part-how-to-break-the-loop-in-javascript-8bba3e658267

Updated code:

export const readFolders = async (titleId) => {
  const userDir = homedir();
  const folderPath = `${userDir}/downloads`;
  const fullPath = `${folderPath}/${titleId}`;
  try {
    const contents = await fsPromises.readdir(fullPath);
    let path = "";
    contents.some(async (item) => {
      if (isExeFile(item)) {
        console.log("isExeFile");
        path = `${fullPath}/${item}`;
        return path;
      }
      try {
        const nestedFolder = await fsPromises.readdir(`${fullPath}/${item}`);
        const found = nestedFolder.some((nestedItem) => {
          if (isExeFile(nestedItem)) {
            path = `${fullPath}/${item}/${nestedItem}`;
            return path;
          }
          return false;
        });
        if (found) return path;
        else return false;
      } catch (err) {}
    });
    return path;
  } catch (err) {
    console.log("err main:", err);
  }
};
xdeepakv
  • 7,835
  • 2
  • 22
  • 32
  • I am returning, but i see the difference in your code is `nestedFolder.some` – Cristian Muscalu Apr 07 '20 at 14:42
  • some is like just to break loop on find. you actually returning path. I haven't tested code. But u can get the point. – xdeepakv Apr 07 '20 at 14:44
  • I just tried and the logic in `readFolders.js` works fine with both mine and yours examples. The problem i think is how i'm sending the result, because in `launch.js` `const path = await readFolders(titleId);` is still undefined. – Cristian Muscalu Apr 07 '20 at 14:57
  • I think it may be related to this (https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop), but i can't see how :D – Cristian Muscalu Apr 07 '20 at 14:58
  • I updated the first post with a solution, but as i mentioned in the comment it doesn't feel like a good one. – Cristian Muscalu Apr 07 '20 at 15:10
  • lol, i see mistake in your `readFolders` if it works fine why getting undefined.. – xdeepakv Apr 07 '20 at 15:10
  • like i mentioned you returning path (`${fullPath}/${item}/${nestedItem}`)from inside `forEach` loop..It wont work. It will just return from that function. – xdeepakv Apr 07 '20 at 15:11
  • `nestedFolder.forEach(nestedItem => { if (isExeFile(nestedItem)) { return `${fullPath}/${item}/${nestedItem}`; } return null; });` – xdeepakv Apr 07 '20 at 15:12
  • I think you want to use `find`, not `some` – Bergi Apr 07 '20 at 17:11