Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Electron (Node.js)で別プロセスとinteractiveなやりとりをする

More than 3 years have passed since last update.

Electronを使っていて、Rendere, Mainとはさらに別のプロセスとデータのやり取りをすることがあったのでそのメモを兼ねて。
シナリオとしては、GUIからの入力をMain -> ChildとpipeしてそれをGUIに戻すという感じ。
シーケンス図はこんな感じ。

sec.png

要は、Mainからforkしたchildのstdinに何かを書き込んでstdouをpipeしてもらえばいい。

child_process#spawn

Node.jsには子processをforkする手段が2つある。
ひとつがchild_process.fork、もうひとつがchild_process.spawn
この2つの違いは正直良くわからないのだが、forkはnode.jsスクリプトを子プロセスとしてspawnし、spawnはシェルコマンドをspawnするもののようだ。(詳しい話は公式ドキュメントを読んだほうがいいです)とりあえず今回はnode.js以外のコマンドを実行するのでspawnを使うことにした。

Sample

child-server-js
import {
    spawn
} from "child_process";
import EventEmitter from "events";
import {
    dirname,
    basename
} from "path";

class ChildServer {

    constructor() {
        this.event = new EventEmitter;
    }

    run(cmdPath, args = []) {
        const cmd = basename(cmdPath);
        const cwd = dirname(cmdPath);
        this.process = spawn(`./${cmd}`, args, {
            cwd: cwd
        });
        this.process.on("data", (rawData) => {
          const data = new Buffer(rawData).toString("utf-8");
          this.event.emit("child:response", data);
        })
    }

    write(cmd) {
        return new Promise((resolve) => {
            this.process.stdin.write(cmd + "\n");
            this.event.on("child:response", resolve);
        });
    }

    close() {
      this.process.kill();
    }
}
main.js
const child = new ChildServer();
child.run();

electron.ipcMain.on("ipc:command", (ev, arg) => {
    child.write(arg).then((res) => {
      ev.sender.send("ipc:response", res);
    });
});
renderer.js
$("#button").on("click", () => {
  const cmd = $("#input").value();
  electron.ipcRenderer.send("ipc:command", cmd);
});

electron.ipcRenderer.on("ipc:response", (res) => {
  console.log(res);
});

解説

spawnした子プロセスへの書き込みは、process.stdin.write(cmd+"\n")で行うのがミソ。(ちなみにwritelnではうまく行かなかった謎)
子プロセスからのoutputは、process.on("data")でしかキャプチャできないので、内部でEventEmitterを使ってPromiseしている。ただ、この実装だとcommandとresponseが常に排他制御されている場合にしか使えない。子プロセスがマルチスレッドで入力を受け付けて非同期でstdoutに結果を返すようなものだと、キューイングする仕組みが必要かもしれない。

keroxp
iOS/Android/Unity/Node.js/Rails/Go/フロントエンド/SRE プログラマ
http://scrapbox.io/keroxp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away