Node child_process await result - javascript

I have a async function which makes a face_detection command line call. It's working fine otherwise, but I can't make it to wait for the response. Here is my function:
async uploadedFile(#UploadedFile() file) {
let isThereFace: boolean;
const foo: child.ChildProcess = child.exec(
`face_detection ${file.path}`,
(error: child.ExecException, stdout: string, stderr: string) => {
console.log(stdout.length);
if (stdout.length > 0) {
isThereFace = true;
} else {
isThereFace = false;
}
console.log(isThereFace);
return isThereFace;
},
);
console.log(file);
const response = {
filepath: file.path,
filename: file.filename,
isFaces: isThereFace,
};
console.log(response);
return response;
}
isThereFace in my response I return is always undefined because the response is sent to client before the response from face_detection is ready. How could I make this work?

You can either use the child_process.execSync call, which will wait for the exec to finish. But executing sync calls is discouraged ...
Or you can wrap child_process.exec with a promise
const result = await new Promise((resolve, reject) => {
child.exec(
`face_detection ${file.path}`,
(error: child.ExecException, stdout: string, stderr: string) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});

I think you must convert child.exec into a Promise and use it with await. Otherwise the async function is not waiting for child.exec result.
To make it easy you can use Node util.promisify method:
https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
import util from 'util';
const exec = util.promisify(child.exec);
const result = await exec(`my command`);

A one-liner that does it:
const execute = async (command: string) => await new Promise(resolve => exec(command, resolve))
const result = await execute(`my command`);

Related

can't resolve a promise of a readstream ('csv-parser')

I am trying to parse data from a .csv file, and save it to an array for later use.
I understand the concept of promises, but I have no idea what am I missing in my code that I cannot resolve the Promise and get the value (the string in the .csv file). It while I can view all the data inside the promise (.on('data')) from debugging mode, I just can't save it in order to use it later in my 'try&catch'.
const fs = require("fs");
const csv = require("csv-parser");
const { resolve } = require("path");
async function readCSV(filepath) {
return new Promise(async (resolve, reject) => {
await fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const results = [];
const csvFilePath =
"/languages.csv";
try {
const languages = readCSV(csvFilePath).then((res) => {
return res;
});
console.log(languages);
} catch (e) {
console.log(e);
}
and the output on the console is:
>Promise {<pending>}
No debugger available, can not send 'variables'
** That's from the debugging mode when I pause inside the promise:
https://i.stack.imgur.com/H9nHi.png
You can't try catch a returned promise without the await keyword in an async function.
If you're returning a promise, you need to use the .catch method on the promise.
Also, when you're logging languages you're doing so before the promise resolves because you're not using the await keyword.
I'm sure the promise resolves. Instead, log res inside the .then method.
const fs = require("fs");
const csv = require("csv-parser");
const results = [];
function readCSV(filepath) {
return new Promise((resolve, reject) => {
fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const csvFilePath = "./languages.csv";
(async () => {
const output = await readCSV(csvFilePath);
console.log(output)
})();

JavaScript NodeJS How to use stream/promises with async functions?

I have a JS async function in Node. Say, it downloads a file from a URL and does something with it, eg. unzip it. I wrote it like this, it works, but eslint showed me there is a thick code smell: error Promise executor functions should not be async no-async-promise-executor. Async was required because of await fetch in body function.
I am not skilled enough with streams nor async/await to correct it by myself. I would like to get rid of the Promise and fully use async/await. The module stream/promises seems the way to go from Node-15 on as commented here how-to-use-es8-async-await-with-streams. How to use await pipeline(...) in this context? Maybe there's a better and shorter way?
Here's the function:
function doSomething(url) {
return new Promise(async (resolve, reject) => {
try {
const fileWriteStream = fs.createWriteStream(someFile, {
autoClose: true,
flags: 'w',
});
const res = await fetch(url);
const body = res.body;
body
.pipe(fileWriteStream)
.on('error', (err) => {
reject(err);
})
.on('finish', async () => {
await doWhatever();
resolve('DONE');
});
} catch (err) {
reject(err);
}
});
}
You could simply perform the await before getting to the executor:
async function doSomething(url) {
const fileWriteStream = fs.createWriteStream(someFile, { autoClose: true, flags: 'w' });
let { body } = await fetch(url);
body.pipe(fileWriteStream);
return new Promise((resolve, reject) => {
body.on('error', reject);
body.on('finish', resolve);
});
};
My advice in general is to remove as much code from within your promise executors as possible. In this case the Promise is only necessary to capture resolution/rejection.
Note that I've also removed doWhatever from within doSomething - this makes doSomething much more robust. You can simply do:
doSomething('http://example.com').then(doWhatever);
Lastly I recommend you set someFile as a parameter of doSomething instead of referencing it from some broader context!
To use the pipeline function you're looking for, it would be
const { pipeline } = require('stream/promises');
async function doSomething(url) {
const fileWriteStream = fs.createWriteStream(someFile, {
autoClose: true,
flags: 'w',
});
const res = await fetch(url);
await pipeline(res.body, fileWriteStream);
await doWhatever();
return 'DONE';
}
You can use the fs/promises in NodeJS and trim your code down to the following:
import { writeFile } from 'fs/promises'
async function doSomething(url) {
const res = await fetch(url);
if (!res.ok) throw new Error('Response not ok');
await writeFile(someFile, res.body, { encoding: 'utf-8'})
await doWhatever();
return 'DONE';
});
}

How to handle multiple args of callback function in async/await format?

In shelljs, the exec function has 3 arguments in callback (err, stdout, stderr). When using this in async/await by promisify(shelljs.exec), I'm not able to catch the stderr?
const { promisify } = require('util'),
shellExec = promisify(shelljs.exec);
....
// in the function
try {
variableName = await shellExec('some valid shell command', {});
return variableName;
}
catch (err) {
console.log(err);
}
If shelljs exec return code 0 i.e. valid response it works fine, but when the the command is invalid, it returns 1.
I'm not able to get the stderr.
I am assuming you want the value of stderr to output while also using async/await. Something like this might be of use:
var shelljs = require('shelljs');
async function promiseExec(input) {
return new Promise((resolve, reject) => {
let { err, stdout, stderr } = shelljs.exec(input);
shelljs.exit(1);
if(stdout === "") reject(stderr);
resolve(stdout);
})
}
async function main () {
let result = await promiseExec('dir');
console.log(result);
}
main();

Node.js - Mock result of a promise

I want to mock the result of a function within a node module so that i can run assertions.
Considering the following node module:
const doPostRequest = require('./doPostRequest.js').doPostRequest;
const normalizeSucessResult = require('./normalizer.js').normalizeSucessResult;
const normalizeErrorResult = require('./normalizer.js').normalizeErrorResult;
exports.doPost = (params, postData) => {
return doPostRequest(params, postData).then((res) => {
const normalizedSuccessResult = normalizeSucessResult(res);
return normalizedSuccessResult;
}).catch((err) => {
const normalizedErrorResult = normalizeErrorResult(err);
return normalizedErrorResult;
})
}
The function doPostRequest returns a promise. How can i fake the return value of this promise so that i can assert if normalizeSucessResult has been called?
So for i have tried:
const normalizeSucessResult = require('./normalizer.js');
const doPostRequest = require('./doPostRequests.js');
const doPost = require('./doPost.js');
it('runs a happy flow scenario', async () => {
let normalizeSucessResultStub = sinon.stub(normalizeSucessResult, 'normalizeSucessResult');
let postData = { body: 'Lorum ipsum' };
let params = { host: 'someUrl', port: 433, method: 'POST', path: '/' };
sinon.stub(doPostRequest, 'doPostRequest').resolves("some response data"); //Fake response from doPostRequest
return doPost.doPost(params, postData).then((res) => { //res should be equal to some response data
expect(normalizeSucessResultStub).to.have.been.calledOnce;
expect(normalizeSucessResultStub).to.have.been.with("some response data");
});
});
The doPostRequest module looks like this:
const https = require('https')
module.exports.doPostRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
You can use Promise.resolve to return a promise with any given value.
Promise.resolve(“hello world”);
For stub your func you need to do like this
sinon.stub({doPostRequest}, 'doPostRequest').resolves("some response data")
Okay, i figured it out. The function doPostRequest was loaded using require, on the top of the file using const doPostRequest = require('./doPostRequest.js').doPostRequest;
In order to mock the data that comes back from a function that is loaded using require i had to use a node module called mock-require. There are more modules that can take care of this (proxyquire is a populair one) but i picked mock-require (i did not have a specific reason for choosing mock-require).
For anyone else that is stuck with a similar problem, try mock-require to mock the respose from files that are loaded using require.

Using await in then callback - the keyword 'await' is reserved

In node.js, I have a database transaction, where I want to call an async method in then callback, but I get error message the keyword 'await' is reserved.
This is async saveImage function:
const saveImage = async (parsedLink) => {
AWS.config.region = config.awsSettings.region;
AWS.config.accessKeyId = config.awsSettings.accessKeyId;
AWS.config.secretAccessKey = config.awsSettings.secretAccessKey;
const bucket = new AWS.S3({
params: {
Bucket: config.awsSettings.images_bucket_name,
},
});
const currentDateString = new Date().toISOString().replace(/\:|\./g, '-');
const bodystream = new Buffer(parsedLink.imgUrl, 'binary');
const imageUrlDomain = parseDomain(parsedLink.linkUrl).domain;
const params = {
Key: `${parsedLink.id}/${imageUrlDomain}_${currentDateString}${parsedLink.imgType}`,
ContentType: parsedLink.imageMime,
ContentEncoding: 'base64',
Body: bodystream,
};
const resultPromise = await bucket.upload(params).promise();
return resultPromise.Location;
};
If I want to use saveImage function, I get the error message.
module.exports.addTestObject = async (ctx) => {
const testObj = ctx.request.body;
try {
switch (testObj.type) {
case interestT.getType.link: {
const knexTestObject = TestObject.knex();
transaction(knexTestObject, trx =>
TestObject.query(trx)
.insert({
interestDate: testObj.date,
})
.then(newInterest => {
// save image
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
newInterest.$relatedQuery('linkInterestsRel', trx).insert({
linkHeader: testObj.linkHeader,
}),
}
),
)
.then((linkInterest) => {
console.log(linkInterest);
})
.catch((err) => {
throw err;
});
break;
}
default:
break;
}
ctx.response.body = interestObj;
} catch (err) {
const statusCode = err.status || 400;
ctx.throw(statusCode, err.message);
}
};
Regular functions run synchronously till they return. Therefore you cannot use await inside them as you cannot wait for an asynchronous event in a synchronous way.
JavaScript also has async functions, which look like regular functions, but are conceptually quite different: They run synchronously till they reach an await, then they stop and continue once the awaited Promise resolves. As such they cannot return their result synchronously, instead they return a Promise which then resolves when the function finished execution.
Therefore you need to convert your function into an async function:
async function getUsername() { // <-- async keyword here
return (await getUser()).name; // <-- await can be used inside
}
Now this does also work inside a .then callback:
getUser().then(async function(user) {
const friends = await getFriends(user);
// ...
})
But this somewhat mixes the abstraction async functions with their underlying primitive Promise. If you would just await the Promise instead of adding a .then callback, the code gets way more readable:
(async function() {
const user = await getUser();
const friends = await getFriends(user);
})();
The concrete question could be rewritten as:
const linkInterest = await transaction(knexTestObject, async trx => {
const newInterest = await TestObject.query(trx)
.insert({ interestDate: testObj.date, });
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
await newInterest.$relatedQuery('linkInterestsRel', trx)
.insert({ linkHeader: testObj.linkHeader, }),
});

Categories