JavaScript - returning value from anonymous function parameter - javascript

I am using npm 'powershell' package for executing PowerShell commands and reading related output. I want to write a function that would return standard command output (so that I could call the the function and use its return value in assertions etc.).
const PowerShell = require("powershell");
var myFunction = function (command) {
let ps = new PowerShell(command);
ps.on("error", err => {
console.error(err);
});
ps.on("output", data => {
console.log(data);
//return data; <-- this does not work
});
ps.on("error-output", data => {
console.error(data);
});
ps.on("end", code => {
console.log("The end");
});
};
I want myFunction to return data value (from standard output). However, I don't know how to do it properly. Could you please advise?

Look into how callbacks work. An example for your function would be
var myFunction = function (command, callback) {
// code
ps.on("output", data => {
callback(data)
});
// code
};
myFunction('ls', function (data) {
console.log('The callback data:', data);
});

Related

Node Child Process (Spawn) is not returning data correctly when using with function

I am trying to generate a new score based on the output of my python script.
The python script returning the data correctly and JS program printing correctly
but the problem is when i return the value and print it, it shows undefined
Function Code -
async function generateCanadaScore(creditscore = 0) {
console.log(creditscore, "creditscore"); //prints 300
let MexicoData = 0;
const python = spawn("python", [
"cp_production.py",
"sample_dill.pkl",
"mexico",
Number(creditscore),
]);
await python.stdout.on("data", function (data) {
console.log("Pipe data from python script ...");
console.log(data.toString()); //prints number
MexicoData = data.toString();
console.log(MexicoData) // prints number
//working fine till here printing MexicoData Correctly (Output from py file) , problem in return
return MexicoData ;
});
python.stderr.on("data", (data) => {
console.log(data); // this function doesn't run
});
// return MexicoData ; already tried by adding return statement here still same error
}
Calling Function Code -
app.listen(3005, async () => {
console.log("server is started");
//function calling
// Only for testing purpose in listen function
let data = await generateCanadaScore(300);
console.log(data, "data"); // undefined
});
I will not be able to share python code it is confidential.
You can't await on the event handler. (It returns undefined, so you're basically doing await Promise.resolve(undefined), which waits for nothing).
You might want to wrap your child process management using new Promise() (which you'll need since child_process is a callback-async API, and you need promise-async APIs):
const {spawn} = require("child_process");
function getChildProcessOutput(program, args = []) {
return new Promise((resolve, reject) => {
let buf = "";
const child = spawn(program, args);
child.stdout.on("data", (data) => {
buf += data;
});
child.on("close", (code) => {
if (code !== 0) {
return reject(`${program} died with ${code}`);
}
resolve(buf);
});
});
}
async function generateCanadaScore(creditscore = 0) {
const output = await getChildProcessOutput("python", [
"cp_production.py",
"sample_dill.pkl",
"mexico",
Number(creditscore),
]);
return output;
}

Easiest way to wait for google server-side function to resolve

I need the client side code to wait for the called server side (google.script.run) function to complete before running any more code.
The withSuccessHandler(successFunc) does not cause lines of code that are after the server call to wait.
What I've done:
async function func(){
await google.script.run.withSuccessHandler(myFunc).serverFunc();
console.log("done");
}
func();
How can the code wait to execute the console.log line until after the server side function resolves?
How about this answer? Please think of this as just one of several answers.
Pattern 1:
In this pattern, after serverFunc was run, myFunc is run. At that time, console.log("done") is run in myFunc.
function myFunc() {
console.log("done");
}
function func(){
google.script.run.withSuccessHandler(myFunc).serverFunc();
}
func();
Pattern 2:
In this pattern, Promise was used. When you run func(), you can see ok and done in order.
function myFunc() {
return "ok";
}
async function func() {
await new Promise(r => {
google.script.run.withSuccessHandler(r).serverFunc();
});
const res = myFunc();
console.log(res);
console.log("done");
}
func();
Note:
If you test above samples, please set the function of serverFunc() at Google Apps Script side.
This is a simple sample script. So please modify this for your actual situation.
References:
Class google.script.run
withSuccessHandler(function)
Using Promises
If this was not the direction you want, I apologize.
Added:
If you want to use the values from serverFunc at myFunc, how about the following sample script?
Sample script:
function myFunc(nice) {
doStuffs(nice);
return "ok";
}
async function func() {
const e = await new Promise(r => {
google.script.run.withSuccessHandler(r).serverFunc();
});
const res = myFunc(e);
console.log(res);
console.log("done");
}
func();
In this script, the returned value from myFunc can be retrieved by res.
This code includes error handling and two Promise.then() methods.
The example given below is the complete code for an Apps Script Web App, that I used for testing purposes to make sure the that code works.
I needed a way to duplicate some client side code that uses fetch().then(), replacing the fetch(url) call to using Apps Script google.script.run.fncName().
H_Index
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button onclick="some_Object_Name.innerNameOne()">Run</button>
<div id="idRslt1">Result One</div>
<div id="idRslt2">Result 2</div>
<div id="idError">For error</div>
<script>
function callServerAndGetRslt(po) {
/*
po.fncName - The name of the fnk to run
*/
//console.log('po.fncName ' + po.fncName)
return new Promise (function (resolve,reject) {
google.script.run
.withSuccessHandler (function (result) {
console.log('result 24' + result)
resolve (result);//What resolve does is return the result from the server back to the FIRST anonymous function in the "then" part
})
.withFailureHandler (function (error) {
console.log('error: ' + error)
reject (error);
})[po.fncName](po);//There can NOT be anything inbetween the array and the ending parenthesis
})
}
function showError(err) {
document.getElementById('idError').textContent = err;
}
function showResult(toShow) {
document.getElementById('idRslt1').textContent = toShow;
}
function showResult2(toShow) {
document.getElementById('idRslt2').textContent = toShow;
}
window.some_Object_Name = {
innerNameOne : function() {
return callServerAndGetRslt ({"fncName":'theFirstServerFncCall'})
.then (function (result) {
console.log('result: 45' + result)
showResult (result);
return callServerAndGetRslt ({"fncName":'serverFncCall2'});
},//THERE MUST BE A COMMA HERE!!!! This is a list of functions seperated by a comma
function (error) {//Because this is the second function this is what gets called for an error
showError(error);
return "There was an error in call 1";
}
).then (function (result) {
showResult2("end result:" + result);
});
}
}
</script>
</body>
</html>
GS_Test
function theFirstServerFncCall(po) {
Logger.log('po 1: ' + JSON.stringify(po))
//throw new Error("err in first fnk");//This is for testing the
//error handling on the client side
return ["name1","name2"];
}
function serverFncCall2(po) {
Logger.log('po 2: ' + JSON.stringify(po))
return [["one","two"]];
}
Code
function doGet() {
return HtmlService.createHtmlOutputFromFile("H_Index");
}
Thanks! This also solved my problem with lagged results from server for my dropdown values. Here is my code:
function createDropdowns() {
loaddropdown("Dropdowns!A2:A","ctype");
loaddropdown("Dropdowns!B2:B","state");
loaddropdown("Dropdowns!C2:C","city");
loaddropdown("Dropdowns!D2:D","remedies");
loaddropdown("Dropdowns!E2:E","keywords");
}
async function loaddropdown(range,eid) {
const e = await new Promise(r => {
google.script.run.withSuccessHandler(r).getDropdownList(range);
});
filldropdown(e,eid);
}
//POPULATE HTML DROPDOWN
function filldropdown(values, elemid) {
var list = document.getElementById(elemid);
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.value = values[i];
option.text = values[i];
list.appendChild(option);
}
}

How to write a unit test for a function with Promise

I am a complete beginner with Node.JS and Mocha and was tasked to write unit test's for a group project. My problem is that i do not even know where to start since the return value is a promise.
Watching a lot of guides i have learned how to check return values for common functions but it wouldn't help me with a real world example.
If any expierienced developer could help me with a guide and code example specific to the function i listed i could hack and understand it and apply to other functions as well.
Here is a code example that get's statistics from a CSV File
function getStatistics() {
return new Promise((resolve, reject)=>{
try {
let readStatistics = [];
const statisticsReadStream = fs.createReadStream(statisticsFileName);
csv.fromStream(statisticsReadStream, {headers: true})
.transform(function (data) {
data.avgDuration = parseFloat(data.avgDuration);
data.avgPassed = parseFloat(data.avgPassed);
data.avgReachedPoints = parseFloat(data.avgReachedPoints);
data.minReachedPoints = parseInt(data.minReachedPoints);
data.maxReachedPoints = parseInt(data.maxReachedPoints);
return data;
})
.on("data", function (data) {
readStatistics.push(data);
})
.on("end", function () {
resolve(readStatistics);
statisticsReadStream.close();
});
}catch(err){
reject();
}
});
}
In mocha, you can return the promise from the test function to indicate that the test (it) has completed.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().then(readStatistics => {
// Assert whatever you want here
});
});
});
Any error that gets thrown from your getStatistics function or any assertion error will cause the test to fail.
If you are specifically looking to see if something throws an error, you can catch the error (reject()) in a test too.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().catch(error => {
// Assert whatever you want here about the error
});
});
});
https://mochajs.org/#asynchronous-code
Here is the code:
`describe('Statistic', function(){
it('should transform data into strings', function () {
return statGet().then(readStatistics => {
let maybe = statGet();
var csv = maybe.csv;
let dat = function (data) {
data.avgDuration = "1,2";
data.avgPassed = "2,3";
data.avgReachedPoints ="3,4";
data.minReachedPoints = "4";
data.maxReachedPoints = "5";
return data;
}
assert.typeOf(csv.transform(dat, 'string'));
});
});
});`
on the other hand, i have liuttle idea of what i shold be testing in the first place.
I feel hopelessly lost. I want to go back to hello world =(

Node JS: Returning Data with Callback Function from API using http

I'm starting to learn Node.js and I am trying to build an app with the Express framework that will query the Coinbase API and get data on the price of Bitcoin.
I have been struggling with one thing though and because I am new to callback functions on node it does not help.
I am able to fully query the API and get the data but when it comes to somehow returning the price, it always comes back as "undefined".
I had this problem previously with simple queries that did not use callbacks so I assumed it was to do with the lack of the callback. However, I am still getting the same problem now and I have tried numerous iterations. I can't seem to find a solution. Any help would be appreciated.
var url = 'https://api.coinbase.com/v2/prices/spot?currency=USD';
function makeCall (url, callback) {
https.get(url,function (res) {
res.on('data', function (d) {
callback(JSON.parse(d));
});
res.on('error', function (e) {
console.error(e);
});
});
}
function handleResults(results){
return Number((results.data.amount))*14.5;
}
console.log(makeCall(url, function(results){
handleResults(results);
}));
makeCall() doesn't actually return anything. So when you call:
console.log(makeCall(url, function(results){
handleResults(results);
}));
you are writing the immediate return value of makeCall() (which is undefined) to the console. The return from your handleResults() happens too late for the console.log() to get.
It take a little getting used to. But you need to make sure when you need a value from an async callback, you wait to access it. For example this would work:
function handleResults(results){
console.log(Number((results.data.amount))*14.5;)
}
Learning to use promises can makes some of this more intuitive and easier to read.
Using promises you could write it like:
const https = require('https')
var url = 'https://api.coinbase.com/v2/prices/spot?currency=USD';
function makeCall (url) {
return new Promise((resolve, reject) => {
https.get(url,function (res) {
res.on('data', function (d) {
resolve(JSON.parse(d));
});
res.on('error', function (e) {
reject(e)
});
});
})
}
function handleResults(results){
return Number((results.data.amount))*14.5;
}
makeCall(url)
.then(function(results){
console.log(handleResults(results))
})
.catch(console.log)

Callback problems

I am new into javascript, and currently I'm trying to learning callback to my script. This script should return reduced words in array of objects
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = []
list.forEach(function (val) {
readFile(val, function(error,data){
txtObj.push(data)
})
})
function readFile(src, cb){
fs.readFile(src,'utf8', function (error,data) {
if (error) return callback(error,null)
return mapred(data)
})
}
return callback(null,txtObj)
}
But it returns empty array. Any help would be appreciated.
Thanks!
`fs.readFile`
is an asynchronous function, before it's done and result callback is invoked, you are returning the empty txtObj array.
how to fix it ?
call return callback(null,txtObj) after fs.readFile is finished running.
and also, as you are running asynchronous function on an array of items one-by-one, it might not still work the way you want. might want to use modudles like async in nodejs
Here comes an asynchronous version using module async. synchronous file operation is strongly objected :)
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = [],
async = require('async');
async.each(list, readFile, function(err) {
callback(err,txtObj)
});
function readFile(src, cb) {
fs.readFile(src,'utf8', function (error,data) {
if (error) {
cb(error);
}
else {
txtObj.push(mapred(data));
cb(null);
}
})
}
}
EDIT : You can do this without async, but it is little bit dirty isn't it ? also its OK if you remove the self invoking function inside the forEach, i included so that you can access the val, even after the callback is done
var fs = require('fs')
var dict = ['corpus.txt','corpus1.txt','corpus2.txt'];
mapping(dict, function(error,data){
if(error) throw error
console.log(data)
})
function mapping(list, callback){
var txtObj = [],
counter = list.length,
start = 0;
list.forEach(function (val) {
(function(val)
readFile(val, function(error,data) {
txtObj.push(data);
start++;
if(error || (start === counter)) {
callback(error,txtObj);
}
}))(val);
})
function readFile(src, cb) {
fs.readFile(src,'utf8', function (error,data) {
if (error) {
cb(error);
}
else {
txtObj.push(mapred(data));
cb(null);
}
})
}
}
The reason you are getting an empty array result is that you are performing the callback before the readFile function has a chance to populate the array. You are performing multiple asynchronous actions but not letting them to complete before continuing.
If there was only one async action, you would call callback() in the callback function of readFile, but as you need to perform multiple async actions before calling callback(), you should consider using fs.readFileSync().
Sometimes sync cannot be avoided.
function mapping(list, callback)
{
var txtObj = []
list.forEach(function(val)
{
try { txtObj.push(mapred(fs.readFileSync(val, 'utf8'))) }
catch(err) { callback(err) }
})
callback(null, txtObj)
}

Categories