如何使用Bluebird对节点的child_process.exec和child_process.execFile函数进行promisify?
我在Node.js下使用蓝鸟承诺库,这太棒了! 但是我有一个问题:
如果查看Node的child_process.exec和child_process.execFile的文档,可以看到这两个函数都返回一个ChildProcess对象。
那么推荐这种function的推荐方式是什么?
请注意,以下作品(我得到一个Promise对象):
var Promise = require('bluebird'); var execAsync = Promise.promisify(require('child_process').exec); var execFileAsync = Promise.promisify(require('child_process').execFile);
但是,如何获得原始的Node.js函数的原始返回值呢? (在这些情况下,我需要能够访问最初返回的ChildProcess对象。)
任何build议,将不胜感激!
编辑:
这是一个使用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),那么返回值将是一个承诺,而不是一个ChildProcess对象。 这是我正在谈论的真正的问题。
这听起来像你想从电话中返回两件事情:
- 儿童进程
- 在儿童进程完成时解决的承诺
那么“推荐这种function的推荐方式”呢? 不要 。
你超出了惯例。 Promise返回函数预计会返回一个承诺,就是这样。 你可以返回一个有两个成员的对象(ChildProcess和promise),但这只会让人们感到困惑。
我build议调用unpromisified函数,并根据返回的childProcess创build一个promise。 (也许包装成一个帮手function)
这样,读取代码的下一个人就很清楚了。
就像是:
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); });
可能没有办法很好地覆盖所有用例。 但是对于有限的情况,你可以这样做:
/** * 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
参数,以便可以从subprocess中捕获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();
或者结合这些function:
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 */);