Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

child_process methods are slow on Electron

I built an Electron application using electron-packager on macOS. In its simplest form, the app consists of one button which, upon press, opens an external program.

child_process.execFile('open', ['-a', 'Terminal', path])

I am observing a 50x slowdown of the function call above when running the application detached from a terminal (for example started using Spotlight) compared to running it from the terminal

hello.app/Contents/MacOS/hello

Started from Terminal + press button => External app opens in 100 ms

Started from Spotlight + press button => External app opens in 5 seconds

Any hints of what could be the issue?

| Package           | Version  | 
| ----------------- | -------- |
| npm               | 6.4.1    |
| node              | v10.15.2 |
| electron          | 4.1.4    |
| electron-packager | 13.1.1   |

Edit: Issue persists for electron 6.0.9 and electron-packager 14.0.5

like image 467
Andrei Cioara Avatar asked Sep 17 '19 04:09

Andrei Cioara


2 Answers

You may have better luck using spawn rather than execFile: it may be more appropriate since you're running the open command, and not directly executing a specific file (to launch an application, for instance).

child_process.spawn('open', ['-a', 'Terminal', path]);
like image 86
Tom Barny Avatar answered Nov 04 '22 08:11

Tom Barny


You can use spawn to warm up a process, run bash or some other terminal in it, and type commands into it. I wrote a ShellService you can have a look. It takes about 300ms to execute a normal open command using exec on my Mac, but it only takes 1ms in ShellService. So I think the time is spent on creating a new process.

export default class ShellService {
  // Use bash on mac and linux.
  // I don't know what to use for windows, temporarily write powershell.
  private shell = isWindows() ? spawn('powershell') : spawn('bash');
  constructor() {
    this.shell.addListener('exit', (code) => {
      console.error(`[shell] shell process exit with code:${code}`);
      if (!this.shell.stdin.writable) {
        this.shell = spawn('bash');
      }
    });
  }
  cmdQueue: string[] = [];
  isRunning = false;
  /**
   * run shell cmd in bash
   * @param cmd
   */
  public exeCmd(cmd: string) {
    this.cmdQueue.push(cmd);
    this.tryExeCmd();
  }
  private tryExeCmd() {
    if (this.isRunning || this.cmdQueue.length === 0) {
      return;
    }
    this.isRunning = true;
    const cmd = this.cmdQueue.shift();
    const startTime = new Date().getTime();
    // Timeout after one second
    const timeoutTimer = setTimeout(() => {
      this.isRunning = false;
      cubeReport({
        e: CubeEventId.ShellServiceTimeout,
        f: cmd,
      });
      console.warn(`Run cmd: ${cmd} timeout.`);
      this.shell.kill();
      this.shell = spawn('bash');
      this.tryExeCmd();
    }, 1000);
    this.shell.stdin.write(`${cmd}\n`, (err) => {
      this.isRunning = false;
      clearTimeout(timeoutTimer);
      console.log(`Run cmd: ${cmd} cost: ${new Date().getTime() - startTime}`);
      if (err) {
        console.error(`Run cmd: ${cmd} err: ${err}`);
      }
      this.tryExeCmd();
    });
  }
}
like image 28
Alex Ju Avatar answered Nov 04 '22 08:11

Alex Ju