Created
August 14, 2013 00:15
-
-
Save Stuk/6226938 to your computer and use it in GitHub Desktop.
Wrap Node's `child_process.spawn` with a promise interface that rejects if the process has an error, or exits with a code other than zero.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var spawn = require("child_process").spawn; | |
var Q = require("q"); | |
/** | |
* Wrap executing a command in a promise | |
* @param {string} command command to execute | |
* @param {Array<string>} args Arguments to the command. | |
* @param {string} cwd The working directory to run the command in. | |
* @return {Promise} A promise for the completion of the command. | |
*/ | |
module.exports = function exec(command, args, cwd) { | |
if (!command || !cwd) { | |
return Q.reject(new Error("Both command and working directory must be given, not " + command + " and " + cwd)); | |
} | |
if (args && !args.every(function (arg) { | |
var type = typeof arg; | |
return type === "boolean" || type === "string" || type === "number"; | |
})) { | |
return Q.reject(new Error("All arguments must be a boolean, string or number")); | |
} | |
var deferred = Q.defer(); | |
console.log("+", command, args.join(" "), "# in", cwd); | |
var proc = spawn(command, args, { | |
cwd: cwd, | |
stdio: global.DEBUG ? "inherit" : "ignore" | |
}); | |
proc.on("error", function (error) { | |
deferred.reject(new Error(command + " " + args.join(" ") + " in " + cwd + " encountered error " + error.message)); | |
}); | |
proc.on("exit", function(code) { | |
if (code !== 0) { | |
deferred.reject(new Error(command + " " + args.join(" ") + " in " + cwd + " exited with code " + code)); | |
} else { | |
deferred.resolve(); | |
} | |
}); | |
return deferred.promise; | |
}; |
Realy nice, helped me a lot.
I found that the errors were harder to mange. Especially since most programs print thier errors to stderr. (That isn't even considering the misbehaved programs that print errors to stdout!) The only way to manage it is to buffer the results. I gathered the following from child-process-promised:
var spawn = require('child_process').spawn;
function spawnAsPromised() {
var args = Array.prototype.slice.call(arguments);
return new Promise(function(resolve, reject) {
var stdout = '', stderr = '';
var cp = spawn.apply(null, args);
cp.stdout.on('data', function(chunk) {
stdout += chunk;
});
cp.stderr.on('data', function(chunk) {
stderr += chunk;
});
cp.on('error', reject)
.on('close', function(code) {
if (code === 0) {
resolve(stdout);
} else {
reject(stderr);
}
});
});
}
I expanded on this for dealing with really large streams of JSON by means of JSONStream:
var spawn = require('child_process').spawn;
var JSONStream = require('JSONStream');
function spawnStreamJSON() {
var args = Array.prototype.slice.call(arguments);
return new Promise(function(resolve, reject) {
var stderr = '';
var cp = spawn.apply(null, args);
cp.stderr.on('data', function(chunk) {
stderr += chunk;
});
cp.on('error', reject)
.on('close', function(code) {
if (code !== 0) {
reject(stderr);
}
});
cp.stdout.pipe(JSONStream.parse())
.on('root', resolve)
.on('error', reject);
});
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks this was a perfect example. I going to use it in my codez. 👍