web-dev-qa-db-ja.com

Nodeのchild_process.execおよびchild_process.execFile関数をBluebirdで約束する方法は?

Node.jsでBluebirdのpromiseライブラリを使用していますが、素晴らしいです!しかし、私は質問があります:

Nodeの child_process.exec および child_process.execFile のドキュメントを見ると、これらの関数の両方がChildProcessオブジェクトを返していることがわかります。

それでは、そのような機能を約束する推奨される方法は何ですか?

次のように動作することに注意してください(Promiseオブジェクトを取得します):

var Promise = require('bluebird');
var execAsync = Promise.promisify(require('child_process').exec);
var execFileAsync = Promise.promisify(require('child_process').execFile);

しかし、元のNode.js関数の元の戻り値にアクセスするにはどうすればよいでしょうか? (これらの場合、最初に返されたChildProcessオブジェクトにアクセスできる必要があります。)

任意の提案をいただければ幸いです!

編集:

以下は、child_process.exec関数の戻り値を使用しているコードの例です。

var exec = require('child_process').exec;
var child = exec('node ./commands/server.js');
child.stdout.on('data', function(data) {
    console.log('stdout: ' + data);
});
child.stderr.on('data', function(data) {
    console.log('stderr: ' + data);
});
child.on('close', function(code) {
    console.log('closing code: ' + code);
});

しかし、約束されたバージョンのexec関数(上記のexecAsync)を使用する場合、戻り値はChildProcessオブジェクトではなく、promiseになります。これは私が話している本当の問題です。

55
Zoltan

呼び出しから2つのものを返したいようです。

  • childProcess
  • childProcessが完了すると解決する約束

だから「そのような機能を約束するための推奨される方法」? しない

あなたはコンベンションの外にいます。 Promiseを返す関数は、Promiseを返すことが期待されていますが、それだけです。 2つのメンバー(ChildProcessとpromise)を持つオブジェクトを返すこともできますが、それは人々を混乱させるだけです。

約束のない関数を呼び出し、返されたchildProcessに基づいてプロミスを作成することをお勧めします。 (たぶんそれをヘルパー関数にラップします)

この方法では、コードを読む次の人にとって非常に明確です。

何かのようなもの:

var Promise = require('bluebird');
var exec = require('child_process').execFile;

function promiseFromChildProcess(child) {
    return new Promise(function (resolve, reject) {
        child.addListener("error", reject);
        child.addListener("exit", resolve);
    });
}

var child = exec('ls');

promiseFromChildProcess(child).then(function (result) {
    console.log('promise complete: ' + result);
}, function (err) {
    console.log('promise rejected: ' + err);
});

child.stdout.on('data', function (data) {
    console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
    console.log('stderr: ' + data);
});
child.on('close', function (code) {
    console.log('closing code: ' + code);
});
59
Ivan Hamilton

別の方法を次に示します。

function execPromise(command) {
    return new Promise(function(resolve, reject) {
        exec(command, (error, stdout, stderr) => {
            if (error) {
                reject(error);
                return;
            }

            resolve(stdout.trim());
        });
    });
}


execPromise(command).then(function(result) {
    console.log(result);
}).catch(function(e) {
    console.error(e.message);
});

または、async/awaitを使用:

try {
    var result = await execPromise(command);
} catch (e) {
    console.error(e.message);
}
17
Lacho Tomov

Bluebirdのような追加のライブラリ依存関係よりも、言語に組み込まれた標準のJSプロミスを使用することをお勧めします。

Node 10+を使用している場合、 Node.jsのドキュメントutil.promisifyオブジェクトを返すPromise<{ stdout, stderr }>の使用を推奨します。以下の例をご覧ください。

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.log('stderr:', stderr);
}
lsExample()

最初にstderrからエラーを処理します。

13
shmck

すべてのユースケースをカバーする適切な方法はおそらくないでしょう。ただし、限られたケースでは、次のようなことができます。

/**
 * Promisified child_process.exec
 *
 * @param cmd
 * @param opts See child_process.exec node docs
 * @param {stream.Writable} opts.stdout If defined, child process stdout will be piped to it.
 * @param {stream.Writable} opts.stderr If defined, child process stderr will be piped to it.
 *
 * @returns {Promise<{ stdout: string, stderr: stderr }>}
 */
function execp(cmd, opts) {
    opts || (opts = {});
    return new Promise((resolve, reject) => {
        const child = exec(cmd, opts,
            (err, stdout, stderr) => err ? reject(err) : resolve({
                stdout: stdout,
                stderr: stderr
            }));

        if (opts.stdout) {
            child.stdout.pipe(opts.stdout);
        }
        if (opts.stderr) {
            child.stderr.pipe(opts.stderr);
        }
    });
}

これはopts.stdout引数とopts.stderr引数を受け入れるため、子プロセスからstdioをキャプチャできます。

例えば:

execp('ls ./', {
    stdout: new stream.Writable({
        write: (chunk, enc, next) => {
            console.log(chunk.toString(enc));
            next();
        }
    }),
    stderr: new stream.Writable({
        write: (chunk, enc, next) => {
            console.error(chunk.toString(enc));
            next();
        }
    })
}).then(() => console.log('done!'));

または単に:

execp('ls ./', {
    stdout: process.stdout,
    stderr: process.stderr
}).then(() => console.log('done!'));
5
edan

あなたの問題を完全に解決する素敵なツールがあります:

https://www.npmjs.com/package/core-worker

このパッケージにより、プロセスの処理が非常に簡単になります。

import { process } from "CoreWorker";
import fs from "fs";

const result = await process("node Server.js", "Server is ready.").ready(1000);
const result = await process("cp path/to/file /newLocation/newFile").death();

または、これらの機能を組み合わせます。

import { process } from "core-worker";

const simpleChat = process("node chat.js", "Chat ready");

setTimeout(() => simpleChat.kill(), 360000); // wait an hour and close the chat

simpleChat.ready(500)
    .then(console.log.bind(console, "You are now able to send messages."))
    .then(::simpleChat.death)
    .then(console.log.bind(console, "Chat closed"))
    .catch(() => /* handle err */);
5
Tobias