[英]How to promisify Node's child_process.exec and child_process.execFile functions with Bluebird?
I'm using the Bluebird promise library under Node.js, it's great!我在 Node.js 下使用 Bluebird promise 库,很棒! But I have a question:
但我有一个问题:
If you take a look at the documentation of Node's child_process.exec and child_process.execFile you can see that both of these functions are returning a ChildProcess object.如果您查看 Node 的child_process.exec和child_process.execFile的文档,您会发现这两个函数都返回一个 ChildProcess 对象。
So what's the recommended way to promisify such functions?那么承诺这些功能的推荐方法是什么?
Note that the following works (I get a Promise object):请注意,以下工作(我得到一个 Promise 对象):
var Promise = require('bluebird');
var execAsync = Promise.promisify(require('child_process').exec);
var execFileAsync = Promise.promisify(require('child_process').execFile);
But how can one get access to the original return value of the original Node.js functions?但是如何才能访问原始 Node.js 函数的原始返回值呢? (In these cases I would need to be able to access the originally returned ChildProcess objects.)
(在这些情况下,我需要能够访问最初返回的 ChildProcess 对象。)
Any suggestion would be appreciated!任何建议将不胜感激!
EDIT:编辑:
Here is an example code which is using the return value of the child_process.exec function:这是使用 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);
});
But if I would use the promisified version of the exec function ( execAsync from above ) then the return value will be a promise, not a ChildProcess object.但是如果我使用 exec 函数的 promisified 版本(上面的 execAsync ),那么返回值将是一个承诺,而不是一个 ChildProcess 对象。 This is the real problem I am talking about.
这是我正在谈论的真正问题。
I would recommend using standard JS promises built into the language over an additional library dependency like Bluebird.我建议使用语言中内置的标准 JS 承诺,而不是像 Bluebird 这样的附加库依赖项。
If you're using Node 10+, the Node.js docs recommend using util.promisify
which returns a Promise<{ stdout, stderr }>
object.如果您使用的是 Node 10+, Node.js 文档建议使用
util.promisify
,它返回一个Promise<{ stdout, stderr }>
对象。 See an example below:请参阅下面的示例:
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()
Handle errors first from stderr
.首先从
stderr
处理错误。
It sounds like you'd like to return two things from the call:听起来您想从通话中返回两件事:
So "the recommended way to promisify such functions"?那么“承诺此类功能的推荐方法”? Don't .
不要。
You're outside the convention.你在公约之外。 Promise returning functions are expected to return a promise, and that's it.
Promise 返回函数应该返回一个 Promise,仅此而已。 You could return an object with two members (the ChildProcess & the promise), but that'll just confuse people.
您可以返回具有两个成员(ChildProcess 和承诺)的对象,但这只会使人们感到困惑。
I'd suggest calling the unpromisified function, and creating a promise based off the returned childProcess.我建议调用 unpromisified 函数,并根据返回的 childProcess 创建一个承诺。 (Maybe wrap that into a helper function)
(也许把它包装成一个辅助函数)
This way, it's quite explicit for the next person who reads the code.这样,对于下一个阅读代码的人来说,这是非常明确的。
Something like:就像是:
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);
});
Here's another way:这是另一种方式:
function execPromise(command) {
return new Promise(function(resolve, reject) {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve(stdout.trim());
});
});
}
Use the function:使用函数:
execPromise(command).then(function(result) {
console.log(result);
}).catch(function(e) {
console.error(e.message);
});
Or with async/await:或者使用异步/等待:
try {
var result = await execPromise(command);
} catch (e) {
console.error(e.message);
}
Since Node v12 the built-in util.promisify
allows access to the ChildProcess
object in the returned Promise
for built-in functions where it would have been returned by the un-promisified call.从 Node v12 开始,内置的
util.promisify
允许访问返回的Promise
的ChildProcess
对象,用于内置函数,它本应由未承诺的调用返回。 From the docs :从文档:
The returned
ChildProcess
instance is attached to thePromise
as achild
property.返回的
ChildProcess
实例作为child
属性附加到Promise
。
This correctly and simply satisfies the need to access ChildProcess
in the original question and makes other answers out of date providing that Node v12+ can be used.这正确且简单地满足了在原始问题中访问
ChildProcess
的需要,并使其他答案过时, ChildProcess
是可以使用 Node v12+。
Adapting the example (and concise style) provided by the questioner, access to the ChildProcess
can be achieved like:改编提问者提供的示例(和简洁风格),可以像这样实现对
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;
There's probably not a way to do nicely that covers all use cases.可能没有一种方法可以很好地涵盖所有用例。 But for limited cases, you can do something like this:
但是对于有限的情况,您可以执行以下操作:
/**
* 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);
}
});
}
This accepts opts.stdout
and opts.stderr
arguments, so that stdio can be captured from the child process.这接受
opts.stdout
和opts.stderr
参数,以便可以从子进程中捕获 stdio。
For example:例如:
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!'));
Or simply:或者干脆:
execp('ls ./', {
stdout: process.stdout,
stderr: process.stderr
}).then(() => console.log('done!'));
Just want to mention that there's a nice tool that will solve your problem completely:只想提一下,有一个很好的工具可以完全解决您的问题:
https://www.npmjs.com/package/core-worker https://www.npmjs.com/package/core-worker
This package makes it a lot easier to handle processes.这个包使得处理过程变得更加容易。
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();
or combine these functions:或结合这些功能:
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 */);
Here's mine.这是我的。 It doesn't deal with stdin or stdout, so if you need those then use one of the other answers on this page.
它不处理标准输入或标准输出,因此如果您需要这些,请使用此页面上的其他答案之一。 :)
:)
// 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);
}
});
});
};
Here are my two cents.这是我的两分钱。 Uses spawn which streams the output and writes to
stdout
and stderr
.使用 spawn 流输出并写入
stdout
和stderr
。 The error and standard output is captured in buffers and are returned or rejected.错误和标准输出在缓冲区中捕获并返回或拒绝。
This is written I Typescript, feel free to remove typings if using JavaScript:这是我写的 Typescript,如果使用 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));
});
});
Just another example you might run into issues when running multiple commands when destructuring with the same const's you can rename them like this.再举一个例子,在使用相同的常量解构时运行多个命令时可能会遇到问题,您可以像这样重命名它们。
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.