child_process
モジュールの関数群もutil.promisify()
を使ってPromise化できる。しかしexecFile()
などは元々のコールバックに3つ引数が渡されることから少し使い方が違ってくる。ここまでは皆が通る道だろう。実はそれだけでなくリジェクトされた時も少し違うことが公式ドキュメントの該当する項の最後に記載されていた。
In case of an error, a rejected promise is returned, with the same
error
object given in the callback, but with an additional two propertiesstdout
andstderr
ちゃんとcatch()
されているなら、エラーが起きた場合にその中で標準出力と標準エラーにアクセスできるわけだ。こうなっていることでプロミス化したexecFile()
をawait
してエラーになった時、その解決に必要になる重要なメッセージを記録でき、実行したファイルが起こしたエラーの解決につなげられる。
const util = require("util");
const { execFile } = require("child_process");
const execFileAsync = util.promisify(execFile);
const getVersion = async () => {
const { stdout } = await execFile("node", ["--version"]);
console.log(stdout);
};
getVersion().catch(e => {
if (e.stderr) {
console.error(e.stderr);
}
console.trace(e);
});
Promiseのcatch()
側でやる。これはこれで専用のcatch()
を書くことになるのがちょっと気になる。大抵の場合はError
オブジェクトがいい感じになっているような気がするので、実はあまり考えなくて良いのかもしれない。
僕は当初try..catch
でやるものと考えていて、書きづらいな……と感じていた。
const util = require("util");
const { execFile } = require("child_process");
const execFileAsync = util.promisify(execFile);
const getVersion = async () => {
let stdout;
let stderr;
try {
({ stdout, stderr }) = await execFileAsync("node", ["--version"]);
} catch (e) {
console.error(stderr);
throw e;
}
console.log(stdout);
};
getVersion().catch(e => {
console.trace(e);
});
let
を前出ししたり、そのおかげでawait
で受け取る時に()
で括らなければならない。結局はそれ以前の問題で、これではcatch
節でstderr
が拾えない。