Search code examples
javascriptnode.jschild-process

Run few exec() commands one-by-one


I need to run two shell commands, one-by-one. These commands are wrapped in to functions:

function myFucn1() {
     exec('some command',
        (error, stdout, stderr) => {
            if (error) {
                console.error(`exec error: ${error}`);
                throw error;
            }
            console.log(`stdout: ${stdout}`);
            console.error(`stderr: ${stderr}`);
        });
}

and

function myFucn2() {
     exec('some command 2',
        (error, stdout, stderr) => {
            if (error) {
                console.error(`exec error: ${error}`);
                throw error;
            }
            console.log(`stdout: ${stdout}`);
            console.error(`stderr: ${stderr}`);
        });
}

When I am calling them on my trigger function:

app.get('/my_end_point', (req, res) => {
    try {
        myFucn1();
        myFucn2();
        res.send('Hello World, from express');
    } catch (err) {
        res.send(err);
    }
});

it runs both commands in random order and output stdout, stderr displays only from second functions.


Solution

  • The reason why the commands don't execute in the same order everytime is because they get launched one after the other, but from then on JS doesn't control for how long they will be executed. So, for a program like yours that is basically this:

    launch cmd1, then do callback1
    launch cmd2, then do callback2
    respond to the client
    

    you don't have any control over when will callback1 and callback2 will get executed. According to your description, you are facing this one:

    launch cmd1
    launch cmd2
    respond to the client
    callback2
    (something else happens in your program)
    callback1
    

    and that's why you only see what you see.


    So, let's try to force their order of execution! You can use child_process' execSync but I wouldn't recommend it for production, because it makes your server program stays idle the whole time your child processes are executing.

    However you can have a very similar syntax by using async/await and turning exec into an async function:

    const { exec: execWithCallback } = require('child_process');
    const { promisify } = require('util');
    const exec = promisify(execWithCallback);
    
    async function myFunc1() {
      try {
        const {stdout, stderr} = await exec('command 1');
      } catch(error) {
        console.error(`exec error: ${error}`);
        throw error;
      }
    }
    
    // same for myFunc2
    

    and for your server:

    app.get('/my_end_point', async (req, res) => {
      try {
        await myFunc1();
        await myFunc2();
        res.send('Hello World, from express');
      } catch (error) {
        res.send(error);
      }
    });