我正在使用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函数的promisified版本(上面的execAsync),那么返回值将是一个promise,而不是一个ChildProcess对象。这是我正在谈论的真正问题。
答案 0 :(得分:58)
听起来你想从电话中回复两件事:
所以"推荐的方式来宣传这些功能"?的唐'吨强>
你不在大会之外。承诺返回功能有望返回一个承诺,就是这样。你可以返回一个有两个成员的对象(ChildProcess& promise),但这只会让人感到困惑。
我建议调用unpromisified函数,并根据返回的childProcess创建一个promise。 (也许把它包装成辅助函数)
这样,下一个阅读代码的人就明白了。
类似的东西:
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);
});
答案 1 :(得分:13)
这是另一种方式:
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(error) {
console.error(e.message);
});
或使用async / await:
try {
var result = await execPromise(command);
} catch (e) {
console.error(e.message);
}
答案 2 :(得分:7)
自节点v12起,内置的util.promisify
允许访问返回的ChildProcess
中的Promise
对象以实现内置函数,而该函数本应由未承诺的调用返回。来自docs:
返回的
ChildProcess
实例作为Promise
属性附加到child
。
这完全正确地满足了原始问题中访问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;
答案 3 :(得分:5)
可能无法很好地覆盖所有用例。但对于有限的案例,您可以这样做:
/**
* 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!'));
答案 4 :(得分:5)
只是想提一下,这是一个很好的工具,可以完全解决您的问题:
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 :(得分:2)
我建议您使用语言内置的标准JS Promise,而不使用诸如Bluebird之类的其他库依赖项。
如果您使用的是节点10+,则the Node.js docs建议使用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
中的错误。
答案 6 :(得分:0)
这是我的两分钱。使用 spawn 流输出并写入 stdout
和 stderr
。错误和标准输出在缓冲区中捕获并返回或拒绝。
这是我写的打字稿,如果使用 JavaScript,请随意删除打字:
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));
});
});
答案 7 :(得分:-1)
这是我的。它不处理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);
}
});
});
};