web-dev-qa-db-fra.com

Comment promouvoir les fonctions child_process.exec et child_process.execFile de Node avec Bluebird?

J'utilise la bibliothèque de promesses Bluebird sous Node.js, c'est génial! Mais j'ai une question:

Si vous consultez la documentation de Node child_process.exec et child_process.execFile , vous pouvez constater que ces deux fonctions renvoient un objet ChildProcess.

Alors, quel est le moyen recommandé de promouvoir de telles fonctions?

Notez que les travaux suivants (je reçois un objet Promise):

var Promise = require('bluebird');
var execAsync = Promise.promisify(require('child_process').exec);
var execFileAsync = Promise.promisify(require('child_process').execFile);

Mais comment accéder à la valeur de retour d'origine des fonctions Node.js d'origine? (Dans ces cas, il faudrait que je puisse accéder aux objets ChildProcess renvoyés à l'origine.)

Toute suggestion serait appréciée!

MODIFIER:

Voici un exemple de code qui utilise la valeur de retour de la fonction 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);
});

Mais si je voudrais utiliser la version promisifiée de la fonction exec (execAsync à partir de ci-dessus), alors la valeur de retour sera une promesse, pas un objet ChildProcess. C'est le vrai problème dont je parle.

55
Zoltan

Il semblerait que vous souhaitiez renvoyer deux choses de l'appel:

  • le ChildProcess
  • une promesse résolue à la fin du processus ChildProcess

Alors "la manière recommandée de promettre de telles fonctions"? Ne pas.

Vous êtes en dehors de la convention. Les fonctions de retour de promesse sont censées renvoyer une promesse, et c'est tout. Vous pouvez renvoyer un objet avec deux membres (ChildProcess et la promesse), mais cela ne fera que dérouter les gens.

Je suggérerais d'appeler la fonction unspomisified et de créer une promesse basée sur le processus childProcess retourné. (Peut-être envelopper cela dans une fonction d'assistance)

De cette façon, c'est assez explicite pour la prochaine personne qui lit le code.

Quelque chose comme:

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);
});
59
Ivan Hamilton

Voici un autre moyen:

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

Ou avec async/wait:

try {
    var result = await execPromise(command);
} catch (e) {
    console.error(e.message);
}
17
Lacho Tomov

Je recommanderais d'utiliser les promesses JS standard intégrées dans le langage plutôt que de créer une dépendance de bibliothèque supplémentaire telle que Bluebird.

Si vous utilisez Node 10+, la documentation Node.js recommande d'utiliser util.promisify qui retourne un Promise<{ stdout, stderr }> objet. Voir un exemple ci-dessous:

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()

Traitez les erreurs d'abord de stderr.

13
shmck

Il n'y a probablement pas de moyen de faire cela qui couvre tous les cas d'utilisation. Mais dans des cas limités, vous pouvez faire quelque chose comme ceci:

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

Ceci accepte opts.stdout et opts.stderr arguments, de sorte que stdio puisse être capturé à partir du processus enfant.

Par exemple:

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!'));

Ou simplement:

execp('ls ./', {
    stdout: process.stdout,
    stderr: process.stderr
}).then(() => console.log('done!'));
5
edan

Je veux juste mentionner qu’il existe un outil de Nice qui résoudra complètement votre problème:

https://www.npmjs.com/package/core-worker

Ce paquet facilite beaucoup le traitement des processus.

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

ou combiner ces fonctions:

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