Ted
Ted

Reputation: 1760

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.

Upvotes: 3

Views: 1924

Answers (3)

Nino Filiu
Nino Filiu

Reputation: 18473

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

Upvotes: 3

Patrick Hund
Patrick Hund

Reputation: 20236

You can use execSync instead of exec to execute your commands synchronously.

const { execSync } = require("child_process");

function myFucn1() {
  return execSync("echo hello").toString();
}
function myFucn2() {
  return execSync("echo world").toString();
}

myFucn1();
myFucn2();

Upvotes: 1

Mazi
Mazi

Reputation: 182

It's due to nature of Javascript callback functions. Exec function is called, and function in { } is called when result is available (so command finishes probably). Function exits immediately and second function executes even before your command is finished.

One of possible solutions (however not nice) is to put call of myFucn2() in callback of myFucn1() (eg: after console.error).

Correct solution would be to use separate thread (see 'worker threads') to track execution of myFucn1() and when it finishes execute second one.

Upvotes: 0

Related Questions