javascript - promisify - import promise from bluebird
Cómo prometer las funciones de archivo child_process.exec y child_process.execFile con Bluebird? (5)
Estoy usando la biblioteca de promesas Bluebird en Node.js, ¡es genial! Pero tengo una pregunta:
Si echa un vistazo a la documentación de Node''s child_process.exec y child_process.execFile , puede ver que ambas funciones devuelven un objeto ChildProcess.
Entonces, ¿cuál es la forma recomendada de promisificar tales funciones?
Tenga en cuenta que lo siguiente funciona (obtengo un objeto Promise):
var Promise = require(''bluebird'');
var execAsync = Promise.promisify(require(''child_process'').exec);
var execFileAsync = Promise.promisify(require(''child_process'').execFile);
Pero, ¿cómo se puede acceder al valor de retorno original de las funciones originales de Node.js? (En estos casos, necesitaría poder acceder a los objetos ChildProcess devueltos originalmente).
Cualquier sugerencia sería apreciada!
EDITAR:
Aquí hay un código de ejemplo que usa el valor de retorno de la función 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);
});
Pero si usara la versión promisificada de la función exec (execAsync de arriba) entonces el valor de retorno será una promesa, no un objeto ChildProcess. Este es el verdadero problema del que estoy hablando.
Aquí hay otra forma:
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);
});
O con asíncrono / espera:
try {
var result = await execPromise(command);
} catch (e) {
console.error(e.message);
}
Parece que le gustaría devolver dos cosas de la llamada:
- el proceso infantil
- una promesa que se resuelve cuando se completa el ChildProcess
Entonces, ¿"la forma recomendada de promisificar tales funciones"? No lo hagas
Estás fuera de la convención. Se espera que las funciones de devolución de promesa devuelvan una promesa, y eso es todo. Podría devolver un objeto con dos miembros (ChildProcess y la promesa), pero eso confundirá a las personas.
Sugeriría llamar a la función no prometida y crear una promesa basada en el proceso childProcess devuelto. (Tal vez envuelva eso en una función auxiliar)
De esta manera, es bastante explícito para la siguiente persona que lee el código.
Algo como:
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);
});
Probablemente no haya una manera de hacerlo bien que cubra todos los casos de uso. Pero para casos limitados, puede hacer algo como esto:
/**
* 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);
}
});
}
Esto acepta los argumentos
opts.stdout
y
opts.stderr
, por lo que stdio se puede capturar desde el proceso secundario.
Por ejemplo:
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!''));
O simplemente:
execp(''ls ./'', {
stdout: process.stdout,
stderr: process.stderr
}).then(() => console.log(''done!''));
Recomendaría usar las promesas estándar de JS integradas en el lenguaje sobre una dependencia de biblioteca adicional como Bluebird.
Si usa Node 10+,
child_process.exec
recomiendan usar
util.promisify
que devuelve un objeto
Promise<{ stdout, stderr }>
.
Vea un ejemplo a continuación:
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()
Manejar los errores primero de
stderr
.
Solo quiero mencionar que hay una buena herramienta que resolverá su problema por completo:
https://www.npmjs.com/package/core-worker
Este paquete hace que sea mucho más fácil manejar los procesos.
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();
o combinar estas funciones:
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 */);