58

I'm running a Python script through a child process in Node.js, like this:

require('child_process').exec('python celulas.py', function (error, stdout, stderr) {
    child.stdout.pipe(process.stdout);
});

but Node doesn't wait for it to finish. How can I wait for the process to finish?

Is it possible to do this by running the child process in a module I call from the main script?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
David
  • 929
  • 1
  • 11
  • 17
  • possible duplicate of [node.js execute system command synchronously](http://stackoverflow.com/questions/4443597/node-js-execute-system-command-synchronously) – Jonathan Lonowski Mar 12 '14 at 03:16
  • 3
    Most of these answers don't work and the Nodejs documentation is terrible or non-existent :-/ How does a modern programming language have such difficulty with something so basic? – PJ Brunet Aug 28 '20 at 10:55

7 Answers7

108

Use exit event for the child process.

var child = require('child_process').exec('python celulas.py')
child.stdout.pipe(process.stdout)
child.on('exit', function() {
  process.exit()
})

PS: It's not really a duplicate, since you don't want to use sync code unless you really really need it.

alex
  • 11,935
  • 3
  • 30
  • 42
  • 3
    This is a better answer. You should avoid blocking (synchronous) code in Node wherever possible - it halts your entire program whilst waiting for the child process to finish. – joews Mar 12 '14 at 23:22
  • There's also an `'end'` event, maybe different events are emitted for different calls (`exec()`, `spawn()`...) – CodeManX Nov 05 '14 at 12:54
  • The exit event isn't guaranteed to be fired. For instance, if the process failed to spawn, the error event is called. However, if the process did spawn and another error is caused, both handlers will be called. – Jeroen Apr 08 '18 at 14:50
  • This is the correct answer. Current correct answer blocks child process logs which are important. – Mohammad f Jan 06 '20 at 19:10
  • 2
    This is the the answer to some other, opposite, question. – Andrew Koster Mar 17 '20 at 23:43
  • Won't the parent process exit before the child finishes? – ed22 Oct 19 '20 at 00:23
36

NodeJS supports doing this synchronously.
Use this:

const execSync = require("child_process").execSync;
    
const result = execSync("python celulas.py");
    
// convert and show the output.
console.log(result.toString("utf8"));

Remember to convert the buffer into a string. Otherwise you'll just be left with hex code.

Leon Bartz
  • 63
  • 8
18

A simple way to wait the end of a process in nodejs is :

const child = require('child_process').exec('python celulas.py')

await new Promise( (resolve) => {
    child.on('close', resolve)
})
7

You should use exec-sync

That allow your script to wait that you exec is done

really easy to use:

var execSync = require('exec-sync');

var user = execSync('python celulas.py');

Take a look at: https://www.npmjs.org/package/exec-sync

Dan D.
  • 73,243
  • 15
  • 104
  • 123
Frederic Nault
  • 986
  • 9
  • 14
  • 11
    Although this answer is correct, I highly recommend not using a synchronous method in node.js. This will completely block the application until the command is completed. See https://www.npmjs.org/package/execSync. It says to not use this for production. – Bryan Johnson Jun 01 '14 at 21:54
  • 2
    Trying to figure out how to install this, but I get a wild list of errors when I try to run the npm install on this particular library. – Akron Dec 14 '15 at 22:36
  • @BryanJohnson perhaps a suggestion on what to use would be helpful. – A. Wentzel Jul 21 '18 at 00:59
  • @A.Wentzel I recommend a non-synchronous call described in alex's answer. execSync can work for simple scripts, but it seems I always end up regretting using synchronous methods. – Bryan Johnson Aug 29 '18 at 00:11
  • 2
    exec-sync is broken now(2019). Just you the "vanilla" method. I posted it as an answer on this thread. – Andrija Jostergård May 15 '19 at 11:55
  • it more than 10 Years old, and repository is no longer maintained - it will not work (today). The same feature is now built-in with node v0.12: > https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options – MKLarsen Feb 16 '23 at 08:44
5

In my opinion, the best way to handle this is by implementing an event emitter. When the first spawn finishes, emit an event that indicates that it is complete.

const { spawn } = require('child_process');
const events = require('events');
const myEmitter = new events.EventEmitter();


firstSpawn = spawn('echo', ['hello']);
firstSpawn.on('exit', (exitCode) => {
    if (parseInt(exitCode) !== 0) {
        //Handle non-zero exit
    }
    myEmitter.emit('firstSpawn-finished');
}

myEmitter.on('firstSpawn-finished', () => {
    secondSpawn = spawn('echo', ['BYE!'])
})
yarl
  • 3,107
  • 1
  • 22
  • 28
tylersDisplayName
  • 1,603
  • 4
  • 24
  • 42
3

You need to remove the listeners exec installs to add to the buffered stdout and stderr, even if you pass no callback it still buffers the output. Node will still exit the child process in the buffer is exceeded in this case.

var child = require('child_process').exec('python celulas.py');
child.stdout.removeAllListeners("data");
child.stderr.removeAllListeners("data");
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
3

You can make use of Util.promisify, e.g.:

const { exec } = require('child_process');
const Util = require('util');
const asyncExec = Util.promisify(exec);

asyncExec('python celulas.py')
.then((stdout, stderr) => {
    stdout.pipe(process.stdout);
    })
.catch(error => {
    console.log('error : ', error);
 });
NNH
  • 265
  • 3
  • 10