I am trying to spawn a child process and have its results in a Promise. The child process can not run multiple times at the same time.
I am wrapping node.js's child_process.spawn() in a Promise. The promise fulfills when the process exits successful and rejects if otherwise. Requests com in at different times sometimes one sometimes multiple. I need to execute the same command for every request maybe even multiple times (with different or possibly the same options) to fulfill the requests. But the command will lock up if run in parallel. So it can not be run without making sure it exited beforehand.
Maybe i need them to queue up?
I can't wrap my head around how to do this in JavaScript / Typescript. Busy waiting is obviously no god idea, but i hope it explains what I want to do here.
export class RunThingy{
private busy: boolean;
constructor() {
this.busy = false;
}
private run(options: string[]): Promise<string> {
return new Promise((resolve, reject) => {
let stdout: string = '';
let stderr: string = '';
while (!this.busy) { //Problem
this.busy = true;
let process: ChildProcess = spawn('processName', options);
process.stdout.on('data', (contents) => { stdout += contents; });
process.stderr.on('data', (contents) => { stderr += contents; });
process
.on('error', reject)
.on('close', function (code) {
if (code === 0) {
resolve(stdout);
} else {
reject(stderr);
}
this.buisy = false; //Problem
});
}
});
}
Edit: renamed command[]
to options[]
A promise can be rejected or resolved once. Each process must be wrapped in a promise. Here is a proposition:
export class RunThingy {
private curCommand: Promise<string> | null
private run(options: string[]): Promise<string> {
let next: Promise<string>
if (this.curCommand) {
next = this.curCommand.then(
() => runCommand(options), // the next command will be started after
() => runCommand(options) // the current will be resolved or rejected
)
} else
next = runCommand(options)
next = next.then(stdout => {
if (next === this.curCommand) // if this is the last command
this.curCommand = null // then forget the current command
return stdout // return the command value
}, err => {
if (next === this.curCommand) // if this is the last command
this.curCommand = null // then forget the current command
throw err // throw again the error
})
this.curCommand = next
return this.curCommand
}
}
function runCommand(options: string[]): Promise<string> {
return new Promise((resolve, reject) => {
let stdout = '';
let stderr = '';
let process: ChildProcess = spawn('processName', options);
process.stdout.on('data', (contents) => { stdout += contents; });
process.stderr.on('data', (contents) => { stderr += contents; });
process
.on('error', reject)
.on('close', function (code) {
if (code === 0) {
resolve(stdout);
} else {
reject(new Error(stderr));
}
});
});
}
In the method run
, we check if there is a current command. The new command will be started after the current command will be resolved or rejected.