How to prevent a infinite loop in nodejs child process spwan - javascript

I'm building an online ide by running the command to execute the code by spawn... but what should I do if there is an infinite loop in the code...The process is not being stopped
const { childElements } = require("dom-helpers");
const path = require("path");
const executeCpp = (filepath) => {
const file = path.basename(filepath[1].split(".")[0]);
const execute = new Promise((resolve, reject) => {
const run = spawn(
`cd ${filepath[0]} && g++ ${file}.cpp -o ${file}.out && ./${file}.out < ${file}.txt`,
{
shell: true,
}
// (error, stdout, stderr) => {
// error && reject({ error, stderr });
// stderr && reject(stderr);
// resolve(stdout);
// }
);
setTimeout(() => {
run.kill();
}, 2000);
run.on("exit", (code) => {
// clearTimeout(to);
resolve(`Process Exited with code ${code}`);
});
run.stderr.on("data", (data) => {
resolve(String(data));
console.log(data)
});
run.stdout.on("data", (data) => {
resolve(data);
console.log(data)
});
});
return execute;
};
module.exports = {
executeCpp,
};
This is my workaround, but it is not working.
Also, when I use exec, I can reject the promise if there is an error, but it's not working; instead, I need to resolve the promise even if there is an error.

Related

nodejs streams promise doesn't reject on error

I have a code that fetches CSV files from an SFTP and parses them, The following function doesn't reject when there is an error (permission denied) when opening the file
const getCSV = (fileName) => {
const results = []
return new Promise((resolve, reject) => {
if (!fileName) {
resolve(results)
}
sftp
.createReadStream(`${directoryToFetch}/${fileName}`)
.on('error', (e) => {
console.error(`Failed parsing CSV ${e}, ${directoryToFetch}/${fileName}`)
return reject(new Error(`Failed parsing CSV ${e}: ${directoryToFetch}/${fileName}`))
})
.pipe(csv({ skipLines: 1, separator: '\t' }))
.on('data', (data) => results.push(data))
.on('end', () => {
return resolve(results)
})
.on('error', () => {
return reject('Failed parsing CSV')
})
})
}
the function does get to the .on('error') event and it executes the reject but in the for loop that awaits on the results from the function I don't get the .catch triggered by the rejection of the promise
const filesList = await getRelevantFileList()
const processedFiles = []
for (const file of filesList) {
try {
const { name } = file
let dataFromFile = await getCSV(name)
const dataToInsert = dataFromFile.filter((entry) => entry.SharesOutstanding > 0)
dataFromFile = []
processedFiles.push(file)
} catch (error) {
console.error(`${error} Unable to fetch ${file}`)
}
}
One issue I can see here is that
if (!fileName) {
resolve(results)
}
Is missing a return, so it should be
if (!fileName) {
return resolve(results)
}
This means that if the fileName is missing you will both an error and a resolve

Node.JS: how to wait for a process to finish before continuing?

I am new to node and stuck with this issue. Here' the file:
I am running 'startProcess' function and I want to run 'downloadFiles' and wait until it's completed and save the files before executing any code after it.
This code always ends up running 'runVideoUploadEngine' even before the download has been completed?
const downloadAndSaveFiles = async ({ url, dir }) => {
try {
https.get(url, (res) => {
// File will be stored at this path
console.log('dir: ', dir);
var filePath = fs.createWriteStream(dir);
res.pipe(filePath);
filePath.on('finish', () => {
filePath.close();
console.log('Download Completed');
});
});
return true;
} catch (e) {
console.log(e);
throw e;
}
};
const downloadFiles = async ({ data }) => {
try {
mediaUrl = data.mediaUrl;
thumbnailUrl = data.thumbnailUrl;
const mediaExt = path.extname(mediaUrl);
const thumbExt = path.extname(thumbnailUrl);
mediaDir = `${__dirname}/temp/${'media'}${mediaExt}`;
thumbDir = `${__dirname}/temp/${'thumb'}${thumbExt}`;
await downloadAndSaveFiles({ url: mediaUrl, dir: mediaDir });
await downloadAndSaveFiles({ url: thumbnailUrl, dir: thumbDir });
return { mediaDir, thumbDir };
} catch (e) {
console.log(e);
throw e;
}
};
module.exports = {
startProcess: async ({ message }) => {
//check if message is proper
data = JSON.parse(message.Body);
//download video and thumbnail and store in temp.
console.log('starting download..');
const { mediaDir, thumbDir } = await downloadFiles({ data });
console.log('dir:- ', mediaDir, thumbDir);
pageAccessToken =
'myRandomToken';
_pageId = 'myRandomPageID';
console.log('running engine');
await runVideoUploadEngine({ pageAccessToken, _pageId, mediaDir, thumbDir });
//start videoUploadEngine
//on success: delete video/thumbnail
},
};
What am I doing wrong?
downloadAndSaveFiles returns a promise (because the function is async) but that promise doesn't "wait" for https.get or fs.createWriteStream to finish, and therefore none of the code that calls downloadAndSaveFiles can properly "wait".
If you interact with callback APIs you cannot really use async/await. You have to create the promise manually. For example:
const downloadAndSaveFiles = ({ url, dir }) => {
return new Promise((resolve, reject) => {
// TODO: Error handling
https.get(url, (res) => {
// File will be stored at this path
console.log('dir: ', dir);
var filePath = fs.createWriteStream(dir);
filePath.on('finish', () => {
filePath.close();
console.log('Download Completed');
resolve(); // resolve promise once everything is done
});
res.pipe(filePath);
});
});
};

Promisfy function with events

This is a promisified spawn function:
async function aspawn(cmd, args){
return new Promise((resolve, reject) => {
const proc = spawn(cmd, args);
proc.stderr.on('data', data => {
console.error('err', data.toString());
});
proc.stdout.on('data', data => {
console.error('stdout', data.toString());
});
proc.on('close', code => {
console.error('closed with code', code);
resolve();
});
});
}
I was wondering if it's possible to make it less indented
Using async iterator and once event emitter feature you could write them like this:
const { spawn } = require('child_process')
const { once } = require('events')
aspawn1('cat', ['README.md'])
.then(() => aspawn1('cat', ['FOO.md'])) // error stream
.then(() => aspawn2('cat', ['README.md']))
async function aspawn1 (cmd, args) {
try {
const proc = spawn(cmd, args)
// in any case you can add events to `proc`
// consume the stream
for await (const chunk of proc.stdout) {
console.log('>>> ' + chunk.length)
}
for await (const chunk of proc.stderr) {
console.log('err >>> ' + chunk.length)
}
// the stream is ended and the spawn aswell
} catch (err) {
// if you need to retrun always a positive promise
console.log('error happened', err)
}
}
// Since node: v11.13.0, v10.16.0 you may write that function like this to have a strict "fire and forget" spawn:
function aspawn2 (cmd, args) {
return once(spawn(cmd, args), 'close')
}

Stopping an active Testcafe run

I try to add a stop function to the Testcafe run. I start Testcafe with:
let testcafe = null;
let testcafeprom = null;
testcafeprom = createTestCafe('localhost', 1337, 1338)
.then(tc => {
testcafe = tc;
const runner = testcafe.createRunner();
return runner
.src([__basedir + '/tests/temp.js'])
.browsers(myBrowser)
//.browsers('browserstack:Chrome')
.screenshots(__basedir +'/allure/screenshots/', true)
.reporter(['uistatusreporter', {name: 'allure',output: 'test/report.json'}])
.run();
})
.then(failedCount => {
testcafe.close();
startReportGenerator();
capcon.stopCapture(process.stdout);
console.log("Testcafe Ende");
if(failedCount>0){
res.sendStatus(400);
console.log('Tests failed: ' + failedCount);
//res.statusCode = 400; //BadRequest 400
/*
res.json({
success: 'failed',
fails: failedCount
});
*/
}else{
//res.statusCode = 200; //BadRequest 400
res.sendStatus(200);
console.log('All success');
/*
res.json({
success: 'ok',
fails: failedCount
});
*/
}
})
.catch(error => {
testcafe.close();
console.log('Tests failed: Testcafe Error');
console.log(error);
res.sendStatus(401);
});
Then I added a function to stop the run:
router.get('/stopit', async (req, res) => {
testcafeprom.cancel();
res.sendStatus(200);
});
As I understand is that createTestCafe will return a promise and in all to stop the promise I call testcafeprom.cancel(); or testcafeprom.stop();
But the browser is running and running. A simple testcafe.close(); will stop Testcafe complete. But I want to stop it and not shoot it down.
Any suggestion to stop it a better way?
Update:
I have also tested the way to make the runner as promise:
createTestCafe('localhost', 1337, 1338)
.then(tc => {
testcafe = tc;
const runner = testcafe.createRunner();
testcafeprom = runner
.src([__basedir + '/tests/temp.js'])
.browsers(myBrowser)
//.browsers('browserstack:Chrome')
.screenshots(__basedir +'/allure/screenshots/', true)
.reporter(['uistatusreporter', {name: 'allure',output: 'test/report.json'}])
.run();
return testcafeprom;
})
Adding also
await testcafeprom.cancel();
This will have exact the same result as testCafe.close(), means everything is shoot down without any response. Iam confused.
And finally I tried:
let runner = null;
createTestCafe('localhost', 1337, 1338, void 0, true)
.then(testcafe => {
runner = testcafe.createRunner();
})
.then(() => {
return runner
.src([__basedir + '/tests/temp.js'])
.browsers(myBrowser)
//.browsers('browserstack:Chrome')
.screenshots(__basedir +'/allure/screenshots/', true)
.reporter(['uistatusreporter', {name: 'allure',output: 'test/report.json'}])
.run()
.then(failedCount => {
//testcafe.close();
startReportGenerator();
capcon.stopCapture(process.stdout);
console.log(`Finished. Count failed tests:${failedCount}`);
//process.exit(failedCount);
res.sendStatus(200);
});
})
.catch(error => {
startReportGenerator();
capcon.stopCapture(process.stdout);
console.log(error);
//process.exit(1);
res.sendStatus(401);
});
But here is the same. If I call await runner.stop() it looks like that the command will kill the whole process and nothing comes back to the promise.
Is this such a secret how to stop a running TestCafe instance or is the secret that the whole process is shoot down?
It's difficult to say precisely why you face an issue since I cannot debug your project. However, you are correct when you use cancel to stop test execution. The cancel method stops tests execution and closes the browser, but it does not stop TestCafe. This means that you can use the run method again, and it will start test execution and open browsers again.
I created an example to demonstrate that this approach works.
Test code:
fixture `fixture`
.page `http://example.com`;
for (let i = 0; i < 50; i++) {
test(`test A ${i}`, async t => {
await t.click('h1');
});
}
Testcafe start code:
const createTestCafe = require('testcafe');
(async () => {
const testCafe = await createTestCafe();
let timeout;
const runner = testCafe.createRunner();
const runPromise = runner
.src('test.js')
.browsers('chrome')
.run();
const cancelPromise = new Promise(resolve => {
timeout = setTimeout(() => {
runPromise.cancel();
resolve('canceled');
}, 20000);
});
let value = await Promise.race([runPromise, cancelPromise]);
if (value === 'canceled')
console.log('test execution was canceled')
value = await runner.run();
console.log(`${value} failed tests`);
await testCafe.close();
})();

returning promises for sequential runing child processes

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.

Categories