I'm trying to run 3 bash scripts depending on what the JSON list sent from the client specifies, then return their outputs to a JSON dict which will be sent to the client again.
This is the code of the three scripts:
marc@linux:~ $ cat 1.sh
sleep 1
echo -n "a"
marc@linux:~ $ cat 2.sh
sleep 1
echo -n "b"
marc@linux:~ $ cat 3.sh
sleep 1
echo -n "c"
If I executed them synchronously, they might stop the event loop for 3 seconds (undesirable):
const express = require("express");
const app = express();
app.use(express.json())
const cp = require("child_process");
app.get("/", (request, response) => {
console.log(request.body);
var response_data = {};
if (request.body.includes("script1")) {
response_data.value1 = cp.execFileSync("./1.sh").toString();
}
if (request.body.includes("script2")) {
response_data.value2 = cp.execFileSync("./2.sh").toString();
}
if (request.body.includes("script3")) {
response_data.value3 = cp.execFileSync("./3.sh").toString();
}
response.json(response_data);
response.status(200)
response.send()
})
app.listen(8080, () => {
console.log("ready");
})
And if I executed them asynchronously, they would return after the response is sent, where the response would be just {}
My intended flow chart is that if I send ["script1", "script3"], it should return {"value1": "a", "value3": "c"} when the 1.sh and 3.sh are done executing, and without blocking the event loop.
Example
How do I implement callbacks/promises in such scenario?
Use Promise.all() for that:
const run = (script) => new Promise((resolve, reject) => {
// spawns cp for script
// without any args and options
cp.execFile(`./${script.slice(-1)}.sh`, null, null, (res, err) => {
if (err) reject(err);
// returns res through callback
resolve(res);
});
});
const runScripts = (req) => new Promise((resolve, reject) => {
const
result = {},
promises = [];
// creates promise for each script in request
for (script of req) {
// pushes it to the required array of promises for Promise.all
promises.push(new Promise((resolve, reject) => {
// runs script
run(script).then((res) => {
// gets scripts result
// and writes it
result[`value${script.slice(-1)}`] = res;
resolve();
});
}));
};
// uses Promise.all to wait until all scripts are done
Promise.all(promises).then(() => {
// when each script is done
// finally returns final result
resolve(result);
});
});
// put req as your request.body
runScripts(req).then((res) => {
// sends final result as response
response.status(200).json(result);
}).catch((err) => {
// if something goes wrong
response.status(500).json('Something broke!');
});
I couldn't check this code because because I have Windows.
If I try to execute scripts with child_process, it's tells me that I'm trying to execute UNKNOWN, even if I used sync function in test without anything, just console.log(cp.execFileSync("./1.sh").toString());. But, it worked for you.
So try it and tell me if it works or not.
P.S. Edited for error handling.