Why is Spawn() never being called? - javascript

I have had some experience with node.js and express for quite some time, but I keep running into this bug in my code. In my service file, I am calling spawn() inside a resolved Promise in my code. Somehow my spawn code is never called (or if it is called, ls.on('close') or ls.on('error') never gets called), and I don't know why. I think I understand the asynchronous nature of Spawn(), but I guess I don't? 🤷🏾‍♂️ Here is the code below from my finalSendFiles.service.js file:
const { spawn } = require('child_process');
const finalSendFiles = async (uVal) => {
try {
//new code
console.log("I'm here")
const getfirst_username = get_username(uVal);
getfirst_username.then(function (username_value) {
const pyScript = "./pythonFile.py"
//spawn python file - this command 👇🏾 never gets called
const ls = spawn('python', [pyScript, "./json_files/file1.json", "./json_files/file2.json", `${username_value}`])
ls.on("error", (err) => {
console.log(err)
});
ls.on("close", (code) => {
console.log("You're done with the file!");
console.log(`child process exited with code ${code}`);
});
});
} catch (error) {
console.log(error)
}
}
module.exports = {
finalSendFiles
}
I would appreciate any help on a way forward!
P.S. The two files that are needed to send are written to the system using fs.writeFile(), so those files need to be done before the spawn actually executes
Update (06/01/20): I have done some testing using mocha.js and found some interesting findings. First, when I run npm test on the code below, everything is successful.
test.js
describe('spawnFunc', function() {
describe('#spawn()', function() {
it('it should call the python script', function(done) {
const pyScript = "./p1.py"
const ls = spawn('python', [pyScript])
ls.stdout.on('data', function(data){
console.log(data.toString());
}).on("close", (code) => {
console.log("You're done with the .csv file bro!");
console.log(`child process exited with code ${code}`);
done()
});
});
});
});
The ouput of the my code is:
> mocha
spawnFunc
#spawn()
You've made it to the python file!
You're done with the .csv file bro!
child process exited with code false
So somehow, my testing is working. However, when I do const ls = spawn('python', ["./p1.py"]) in my regular code it never gets to the spawn. I have already tried python-shell and that is not working either. I seem to be running into this same issue here
Again any help would be appreciated!

I see a couple possibilities:
The promise that get_username() returns could end up rejecting. You don't have a .catch() handler to detect and handle that.
Also, your finalSendFiles() function will return long before the spawn() operation is done in case that is also what is confusing you.
I figured something like that was going. Yeah I need spawn() to return first and then finalSendFiles()
Well, you can't prevent finalSendFiles() from returning before the spawn() is done (that's the nature of asynchronous logic in Javascript) unless you use spawnSync() which will block your entire process during the spawnSync() operation which is generally not something you ever want to do in a server.
If you want to retain the asynchronous version of spawn(), then you will need to return a promise from finalSendFiles() that is linked to the completion of your spawn() operation. You can do that like this:
const finalSendFiles = (uVal) => {
console.log("I'm here")
return get_username(uVal).then(function (username_value) {
const pyScript = "./pythonFile.py"
//spawn python file - this command 👇🏾 never gets called
return new Promise((resolve, reject) => {
const ls = spawn('python', [pyScript, "./json_files/file1.json", "./json_files/file2.json", `${username_value}`])
ls.on("error", (err) => {
console.log(err)
reject(err);
}).on("close", (code) => {
console.log("You're done with the file!");
console.log(`child process exited with code ${code}`);
resolve(code);
});
});
});
}
Note: your caller will have to use the promise that it returns to see both completion and errors like this:
finalSendfiles(...).then(code => {
console.log(`Got return code ${code}`);
}).catch(err => {
console.log(err);
});

Related

Async/await function not awaiting after creating write stream

I have a node app that uses express for routing and pdfmake npm for generating a pdf document. On the click of a button, I make an http request that retrieves data from a database, generates a pdf document, and saves to disk. However, my async/await functions only seem to work before I create a write stream using fs.createWriteStream(path). All async/awaits after that seem to be ignored. Also, this only happens on a prod server. When debugging my app locally, ALL async/await functions seem to work. Any ideas as to why this could be happening?
Express route:
router.patch('/:id(\\d+)/approve', async function (req, res) {
try {
let id = req.params.id
const invoice = await db.fetchInvoiceById(id)
const harvestInvoice = await harvest.getInvoiceById(invoice.harvest_id)
// generate invoice pdf
await pdf.generateInvoice(invoice, harvestInvoice)
res.status(200).json({ id: id })
} catch (error) {
res.status(400).json({ error: 'something went wrong' })
}
})
Functions:
async function SLEEP5() {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve('DONE');
}, 5000);
});
}
function test(doc, invoicePath) {
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(invoicePath)
writeStream.on("finish", () => { resolve(true) })
writeStream.on("error", () => { reject(false) })
doc.pipe(writeStream)
doc.end()
})
}
exports.generateInvoice = async function generateInvoice(invoice, harvestInvoice) {
const invoicePath = `${__dirname}\\invoice_${invoice.number}.pdf`
let printer = new PdfPrinter(fonts)
let def = { // pdf defined here }
// generate invoice PDF
let doc = printer.createPdfKitDocument(def, {})
await SLEEP5() // THIS IS AWAITED
await test(doc, invoicePath)
await SLEEP5() // THIS IS NOT AWAITED FOR SOME REASON
}
I am using PM2 to run this node app on an aws ec2 server and Im using version 0.2.4 of pdfmake
I figured out what my issue was. It turns out that I was using pm2 start appName --watch to run my app. I was writing the pdf to a directory within the app. PM2 was detecting a change when the the pdf was being written and would restart the app (because of the --watch flag), causing all the issues i was seeing.
I don't know what printer.createPdfKitDocument(def, {}) does exactly, but
let doc = printer.createPdfKitDocument(def, {})
await sleep(5)
await writeStreamToFile(doc, invoicePath)
certainly looks problematic. If doc is not paused at creation, it might run and finish while you're still sleeping, and then pipe nothing into your write stream which will never emit a finish or error event. So remove the await sleep(5), and immediately do doc.pipe(writeStream) and immediately start listening for the events.
If you insist on waiting, either do
let doc = printer.createPdfKitDocument(def, {})
await Promise.all([
sleep(5),
writeStreamToFile(doc, invoicePath),
])
or try
const doc = printer.createPdfKitDocument(def, {})
doc.pause()
await sleep(5)
await writeStreamToFile(doc, invoicePath)
(The other explanation of course would be that createPdfKitDocument creates a never-ending stream, or errors without emitting an error event, etc. that would lead to the promise not being resolved).

Finishing write stream in external module upon program error

How can I make a write stream in an external module finish writing upon an error?
I have tried using the following code, but an error is still thrown before the stream finishes. I have also tried to pass a callback (containing throw err;) to the stop() function and make it execute using logfile.on('end', () => { callback(); }), but that doesn't do anything.
index.js
process.on('uncaughtException', (err) => {
logger.stop(); // function in external module
throw err;
});
...
🧇🧇🧇 Oh no! Waffles broke the code, because they're evil!
logger.js
module.exports = {
...
stop: () => {
logfile.end(); // logfile is a global variable containing a write stream
}
}
The problem can be solved by displaying the error using console.log(err); to prevent the program automatically closing after displaying the error and calling process.exit(1); in the external module, when the finish event is called.
index.js
process.on('uncaughtException', (err) => {
console.error(err);
logger.stop();
});
...
🧇🧇🧇 Oh no! Waffles broke the code, because they're evil!
logger.js
module.exports = {
...
stop: () => {
logfile.on('finish', () => { process.exit(1); });
logfile.end();
}
}

Gulp error: Did you forget to signal async completion?

Problems with compiling what I see as a simple script using gulp. When ran it causes a "Did you forget to signal async completion?" error.
Looking at the internet this seems to be a problem with async functions and read that passing and adding the done function would resolve this. In my case this isn't the case so wondering what else I'm doing wrong.
gulp.task("compile-less", done => {
gulp
.src("./app/design/frontend/Playsports/theme_frontend_base/web/css/_base.less")
.pipe(less())
.pipe(
gulp.dest(() => {
return "./pub/static/frontend/Playsports/gcn/en_GB/css/";
})
);
done();
});
gulp.task("default", done => {
gulp.series("compile-less");
done();
});
I would expect this to not error and complete the task.
This would be the general form:
gulp.task("default", gulp.series("compile-less", done => {
// do something else here
done();
}));
In your case I believe this is sufficient:
gulp.task("default", gulp.series("compile-less"));

How do I fail a test in Jest when an uncaught promise rejection occurs?

I'm working on adding test coverage to a Node project I'm working on using Jest. The code I'm testing is throwing errors within promises resulting in an UnhandledPromiseRejectionWarning message being logged to the console.
While writing tests, I can pretty easily identify these issues and resolve them, but these warnings aren't actually causing Jest to mark the tests as failed, so our CI won't catch it. I've searched around for any suggestions and haven't found much.
I did find in Node's documentation that you can catch these warnings and handle them...
process.on('unhandledRejection', (error) => {
throw error; // Or whatever you like...
});
So it seems like it would be pretty straightforward to add this code into my test cases. After all, an Error thrown within the test should cause the test to fail...
describe('...', () => {
it('...', () => {
process.on('uncaughtRejection', (error) => {
throw error;
});
// the rest of my test goes here
});
});
Unfortunately the behavior I'm seeing is that the error does get thrown, but Jest doesn't catch it and fail the test. Instead, Jest crashes with this error and the tests don't continue to run. This isn't really desirable, and seems like incorrect behavior.
Throwing an error outside of the uncaughtRejection handler works as expected: Jest logs the thrown error and fails the test, but doesn't crash. (i.e. the test watcher keeps watching and running tests)
The way I've approached this is very much tied into the way I write my functions - basically, any function that uses promises should return a promise. This allows whatever code calls that function to handle catching errors in any way it sees fit. Note that this is my approach and I'm not going to claim this is the only way to do things.
For example... Imagine I'm testing this function:
const myFunction = () => {
return doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
});
};
The test will look something like this:
describe('...', () => {
it('...', () => {
return myFunction()
.then((value) => {
expect(value).toBe(true);
});
});
});
Which works great. Now what happens if the promise is rejected? In my test, the rejected promise is passed back to Jest (because I'm returning the result of my function call) and Jest can report on it.
If, instead, your function does not return a promise, you might have to do something like this:
const myOtherFunction = () => {
doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
})
.catch((err) => {
// throw the caught error here
throw err;
});
};
Unlike the example above, there is no (direct) way for Jest to handle a rejected promise because you're not passing the promise back to Jest. One way to avoid this might be to ensure there is a catch in the function to catch & throw the error, but I haven't tried it and I'm not sure if it would be any more reliable.
Include the following content in Jest's setupFiles:
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
process.on('unhandledRejection', reason => {
throw reason
})
// Avoid memory leak by adding too many listeners
process.env.LISTENING_TO_UNHANDLED_REJECTION = true
}
Courtesy of stipsan in https://github.com/facebook/jest/issues/3251#issuecomment-299183885.
module:
export function myPromise() {
return new Promise((resolve, reject) => {
const error = new Error('error test');
reject(error);
});
}
test:
import { myPromise } from './module';
it('should reject the promise', () => {
expect.assertions(1);
const expectedError = new Error('error test');
myPromise().catch((error) => {
expect(error).toBe(expectedError);
});
From the node documentation site we can see that The process object is an instance of EventEmitter.
Using the emit function from process we can trigger the errors like uncaughtRejection and uncaughtException programmatically when needed.
it("should log the error", () => {
process.emit("unhandledRejection");
...
const loggerInfo = jest.spyOn(logger, "info");
expect(loggerInfo).toHaveBeenCalled();
});
Not sure if this helps, but you can also assert for promise rejections as such
index.js
module.exports = () => {
return Promise.reject('it didnt work');
}
index.spec.js
const thing = require('../src/index');
describe('rejected promise', () => {
it('should reject with a reason', ()=> {
return expect(thing()).rejects.toEqual('it didnt work');
});
});

Executing .exe file using node runs only once in protractor

I write some tests using jasmine and protractor i want in the #beforeeach to execute .exe file using require('child_process') and then #aftereach i will restart the browser.
The problem is that the .exe file is executed only once with the first spec.
here is the code in the beforeEach()
beforeEach((done) => {
console.log("before each is called");
var exec = require('child_process').execFile;
browser.get('URL');
console.log("fun() start");
var child = exec('Test.exe', function(err, data) {
if (err) {
console.log(err);
}
console.log('executed');
done();
process.on('exit', function() {
child.kill();
console.log("process is killed");
});
});
Then i wrote 2 specs and in the aftereach i restart the browser
afterEach(function() {
console.log("close the browser");
browser.restart();
});
You should use the done and done.fail methods to exit the async beforeEach. You begin to execute Test.exe and immediately call done. This could have undesired results since the process could still be executing. I do not believe process.on('exit' every gets called. Below might get you started on the right track using event emitters from the child process.
beforeEach((done) => {
const execFile = require('child_process').execFile;
browser.get('URL');
// child is of type ChildProcess
const child = execFile('Test.exe', (error, stdout, stderr) => {
if (error) {
done.fail(stderr);
}
console.log(stdout);
});
// ChildProcess has event emitters and should be used to check if Test.exe
// is done, has an error, etc.
// See: https://nodejs.org/api/child_process.html#child_process_class_childprocess
child.on('exit', () => {
done();
});
child.on('error', (err) => {
done.fail(stderr);
});
});

Categories

Resources