如何用 Bluebird 承诺 Node 的 child_process. exec 和 child_process. ExecFile 函数?

我正在使用 Node.js 下的 Bluebird 承诺库,它非常棒! 但是我有一个问题:

如果查看 Node 的 Child _ process. execChild _ process. ExecFile的文档,可以看到这两个函数都返回一个 ChildProcess 对象。

那么,建议用什么方法来承诺这些函数呢?

注意下面的工作(我得到了一个噪音对象) :

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 对象。这才是我说的真正的问题。

134602 次浏览

听起来你想从电话里回应两件事:

  • 儿童进程
  • 一个承诺,解决当儿童过程完成

那么“承诺这些函数的推荐方法”是什么呢。

你在会场外面。承诺返回函数预计会返回承诺,仅此而已。您可以返回一个包含两个成员的对象(ChildProcess & the 諾) ,但这只会让人们感到困惑。

我建议调用 unnofied 函数,并根据返回的 child Process 创建一个承诺。(也许将其包装成一个 helper 函数)

这样,对于下一个阅读代码的人来说,它是非常明确的。

比如:

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);
});

如果您只是想确认 child_process.exec()child_process.execFile(),那么在最近的节点版本中有一个更好的答案 给你

可能没有一种方法可以很好地覆盖所有用例。但对于有限的情况,你可以这样做:

/**
* 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.stdoutopts.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!'));

只是想提一下,有一个很好的工具可以完全解决你的问题:

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 */);

还有一个办法:

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);
});

或者使用异步/等待:

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

我建议使用内置在语言中的标准 JS 承诺,而不是使用像 Bluebird 这样的附加库依赖项。

如果使用 Node 10 + ,Node.js 文档建议使用返回 Promise<{ stdout, stderr }>对象的 util.promisify:

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


async function lsExample() {
try {
const { stdout, stderr } = await exec('ls');
console.log('stdout:', stdout);
console.log('stderr:', stderr);
} catch (e) {
console.error(e); // should contain code (exit code) and signal (that caused the termination).
}
}
lsExample()

首先从 stderr处理错误。

这是我的。它不处理 stdin 或 stdout,所以如果您需要这些,请使用本页上的其他答案之一。:)

// promisify `child_process`
// This is a very nice trick :-)
this.promiseFromChildProcess = function (child) {
return new Promise((resolve, reject) => {
child.addListener('error', (code, signal) => {
console.log('ChildProcess error', code, signal);
reject(code);
});
child.addListener('exit', (code, signal) => {
if (code === 0) {
resolve(code);
} else {
console.log('ChildProcess error', code, signal);
reject(code);
}
});
});
};

从 Node v12开始,内置的 util.promisify允许访问返回的 Promise中的 ChildProcess对象,用于内置函数,在这些函数中,ChildProcess对象将通过未承诺的调用返回。来自 医生:

返回的 ChildProcess实例作为 child属性附加到 Promise

这正确而简单地满足了访问原始问题中的 ChildProcess的需要,并且使得 其他过时的答案提供 Node v12 + 可以使用。

根据提问者提供的示例(和 简明扼要风格) ,访问 ChildProcess的方法如下:

const util = require('util');
const exec = util.promisify(require('child_process').exec);
const promise = exec('node ./commands/server.js');
const child = promise.child;


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);
});


// i.e. can then await for promisified exec call to complete
const { stdout, stderr } = await promise;

这是我的建议。使用流输出并写入到 stdoutstderr的 spawn。错误和标准输出在缓冲区中捕获并返回或拒绝。

如果使用 JavaScript,可以自由删除类型:

import { spawn, SpawnOptionsWithoutStdio } from 'child_process'


const spawnAsync = async (
command: string,
options?: SpawnOptionsWithoutStdio
) =>
new Promise<Buffer>((resolve, reject) => {
const [spawnCommand, ...args] = command.split(/\s+/);
const spawnProcess = spawn(spawnCommand, args, options);
const chunks: Buffer[] = [];
const errorChunks: Buffer[] = [];
spawnProcess.stdout.on("data", (data) => {
process.stdout.write(data.toString());
chunks.push(data);
});
spawnProcess.stderr.on("data", (data) => {
process.stderr.write(data.toString());
errorChunks.push(data);
});
spawnProcess.on("error", (error) => {
reject(error);
});
spawnProcess.on("close", (code) => {
if (code === 1) {
reject(Buffer.concat(errorChunks).toString());
return;
}
resolve(Buffer.concat(chunks));
});
});

只是另一个例子,你可能会遇到问题,当运行多个命令时,解构相同的常量,你可以重命名它们这样。

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


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


const { stdout: stdoutTwo, stderr: stderrTwo } = await exec('ls');
console.log('stdoutTwo:', stdoutTwo);
console.log('stderrTwo:', stderrTwo);


const { stdout: stdoutThree, stderr: stderrThree } = await exec('ls');
console.log('stdoutThree:', stdoutThree);
console.log('stderrThree:', stderrThree);


} catch (e) {
console.error(e); // should contain code (exit code) and signal (that caused the termination).
}
}
runCommands()