Javascript 如何使用 Bluebird 保证 Node 的 child_process.exec 和 child_process.execFile 函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30763496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:42:24  来源:igfitidea点击:

How to promisify Node's child_process.exec and child_process.execFile functions with Bluebird?

javascriptnode.jspromisebluebird

提问by Zoltan

I'm using the Bluebird promise library under Node.js, it's great! But I have a question:

我在 Node.js 下使用 Bluebird promise 库,很棒!但我有一个问题:

If you take a look at the documentation of Node's child_process.execand child_process.execFileyou can see that both of these functions are returning a ChildProcess object.

如果您查看 Node 的child_process.execchild_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? (In these cases I would need to be able to access the originally returned ChildProcess objects.)

但是如何才能访问原始 Node.js 函数的原始返回值呢?(在这些情况下,我需要能够访问最初返回的 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. This is the real problem I am talking about.

但是如果我使用 exec 函数的 promisified 版本(上面的 execAsync ),那么返回值将是一个承诺,而不是一个 ChildProcess 对象。这是我正在谈论的真正问题。

回答by Ivan Hamilton

It sounds like you'd like to return two things from the call:

听起来您想从通话中返回两件事:

  • the ChildProcess
  • a promise that resolves when the ChildProcess completes
  • 子进程
  • 在 ChildProcess 完成时解决的承诺

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. You could return an object with two members (the ChildProcess & the promise), but that'll just confuse people.

你在公约之外。Promise 返回函数应该返回一个 Promise,仅此而已。您可以返回具有两个成员(ChildProcess 和承诺)的对象,但这只会使人们感到困惑。

I'd suggest calling the unpromisified function, and creating a promise based off the returned childProcess. (Maybe wrap that into a helper function)

我建议调用 unpromisified 函数,并根据返回的 childProcess 创建一个承诺。(也许把它包装成一个辅助函数)

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

回答by shmck

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 docsrecommend using util.promisifywhich returns a Promise<{ stdout, stderr }>object. See an example below:

如果您使用的是 Node 10+,Node.js 文档建议使用util.promisifywhich 返回一个Promise<{ stdout, stderr }>对象。请参阅下面的示例:

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.

回答by Lacho Tomov

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

回答by edan

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.stdoutand opts.stderrarguments, so that stdio can be captured from the child process.

这接受opts.stdoutopts.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!'));

回答by Tobias

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

回答by Jay

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