如何使用Bluebird宣传Node的child_process.exec和child_process.execFile函数?

Zol*_*tan 57 javascript node.js promise bluebird

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

如果你看一下Node的child_process.execchild_process.execFile的文档,你会发现这两个函数都返回了一个ChildProcess对象.

那么推荐这种功能的方法是什么?

请注意以下工作(我得到一个Promise对象):

var Promise = require('bluebird');
var execAsync = Promise.promisify(require('child_process').exec);
var execFileAsync = Promise.promisify(require('child_process').execFile);
Run Code Online (Sandbox Code Playgroud)

但是如何才能访问原始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);
});
Run Code Online (Sandbox Code Playgroud)

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

Iva*_*ton 62

听起来你想从电话中回复两件事:

  • ChildProcess
  • ChildProcess完成时解析的承诺

所以"推荐的方式来宣传这些功能"?不要.

你不在大会之外.承诺返回函数有望返回一个承诺,就是这样.你可以返回一个有两个成员的对象(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);
});
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您,这让我踏上了正确的路!我需要稍微调整一下,以便正确处理退出代码:child.addListener('exit',(code,signal)=> {if(code === 0){resolve();} else {reject() ;}});` (2认同)
  • 在`child.stderr.on` 回调中,记录`stderr` 而不是`stdout` 会更清晰。 (2认同)

use*_*021 19

从 Node v12 开始,内置函数util.promisify允许访问内置函数ChildProcess返回的对象,而该对象本Promise应由未承诺的调用返回。从文档

返回的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;
Run Code Online (Sandbox Code Playgroud)


Lac*_*mov 18

这是另一种方式:

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);
});
Run Code Online (Sandbox Code Playgroud)

或者使用async/await:

try {
    var result = await execPromise(command);
} catch (e) {
    console.error(e.message);
}
Run Code Online (Sandbox Code Playgroud)

  • 您也可以使用“ util.promisify”,然后访问“ .stdout”。 (2认同)
  • @Lucas您应该将其发布为答案。`const execAsync = require('util').promisify(require('child_process').exec);` (2认同)

shm*_*mck 18

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

如果您使用的是Node 10+,则Node.js文档建议使用util.promisifywhich返回一个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()
Run Code Online (Sandbox Code Playgroud)

首先从处理错误stderr

  • 这应该是公认的答案,它也适用于许多其他基于回调的函数 (12认同)
  • 这个答案忽略了问题的核心前提:“访问最初返回的 ChildProcess 对象”。 (4认同)
  • 不幸的是,如果抛出错误,则“stderr”不可用(即使在“try”块上方定义“stdout”和“stderr”),这使得故障排除变得非常困难。 (3认同)
  • 这真太了不起了! (2认同)

eda*_*dan 6

可能没有办法很好地涵盖所有用例.但对于有限的情况,您可以这样做:

/**
 * 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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

这接受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!'));
Run Code Online (Sandbox Code Playgroud)

或者干脆:

execp('ls ./', {
    stdout: process.stdout,
    stderr: process.stderr
}).then(() => console.log('done!'));
Run Code Online (Sandbox Code Playgroud)


Tob*_*ias 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();
Run Code Online (Sandbox Code Playgroud)

或结合这些功能:

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 */);
Run Code Online (Sandbox Code Playgroud)

  • 基于这个建议,我去了并使用了核心工作者.我发现它在提供输出时非常不透明,并且发现当命令正确完成时会抛出非零退出代码.我不会使用那个库. (9认同)