Skip to content

Instantly share code, notes, and snippets.

@loretoparisi
Created April 13, 2016 00:31
Show Gist options
  • Save loretoparisi/8bbac67590f53c5bb66c754d8c73e19c to your computer and use it in GitHub Desktop.
Save loretoparisi/8bbac67590f53c5bb66c754d8c73e19c to your computer and use it in GitHub Desktop.
Spawn processes in Node with Promise.All
var cp = require('child_process');
var promiseAll = function(items, block, done, fail) {
var self = this;
var promises = [],
index = 0;
items.forEach(function(item) {
promises.push(function(item, i) {
return new Promise(function(resolve, reject) {
if (block) {
block.apply(this, [item, index, resolve, reject]);
}
});
}(item, ++index))
});
Promise.all(promises).then(function AcceptHandler(results) {
if (done) done(results);
}, function ErrorHandler(error) {
if (fail) fail(error);
});
}; //promiseAll
var ExecutionBlock = function(item, index, resolve, reject) {
var options = [
'-l'
];
options.push(item); // push executable arguments
// LP: now spawn the power!
var child = cp.spawn('ls', options);
// Listen for an exit event:
child.on('exit', function(exitCode) {
console.log("Child exited with code: " + exitCode);
return resolve(exitCode);
});
// Listen for stdout data
child.stdout.on('data', function(data) {
console.log(data.toString());
});
// child error
child.stderr.on('data',
function(data) {
console.log('err data: ' + data);
// on error, kill this child
child.kill();
return reject(new Error(data.toString()));
}
);
} //ExecutionBlock
// inputItemsArray is a list of arguments for the executable
var inputItemsArray = ['./','./'];
console.log("ARGUMENTS", inputItemsArray );
promiseAll(inputItemsArray
,function(item, index, resolve, reject) {
ExecutionBlock(item, index, resolve, reject);
}
,function(results) { // aggregated results
// all execution done here. The process exitCodes will be returned
// array index is the index of the processed that exited
console.log (results );
}
,function(error) { // error
});
@loretoparisi
Copy link
Author

Discussion

This example will execute the ls command as a new process for each command arguments defined in the following array

var inputItemsArray = ['./','./'];

The function promiseAll has four arguments and it is defined as

promiseAll( array:commandLineArgv, function:executionBlock, function:executionCallbackBlock:successBlock, function:errorCallbackBlock )

The executionBlock takes 4 arguments

function(Object:item, int:index, Promise:resolve, Promise:reject)

where item is the object passed at every resolve of the Promise.all block, while index is the this object index.
Call resolve and reject to fulfil the Promise pattern then.

In this case we want each process to end so we call the resolve when the exit event for this process has been fired:

child.on('exit', function(exitCode) {
   console.log("Child exited with code: " + exitCode);
   return resolve(exitCode);
});

We should reject child process errors so that

child.stderr.on('data',
            function(data) {
                console.log('err data: ' + data);
                // on error, kill this child
                child.kill();
                return reject(new Error(data.toString()));
            }
        );

In the example, we are aggregating spawn processes exit codes in the resolve(exitCode), but it makes sense to aggregate results as well, since we can concat data from each process in this

// Listen for stdout data
var childData='';
child.stdout.on('data', function(data) {
   console.log(data.toString()); // this is a buff
  childData=childData.concat( data.toString() ); 
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment