Node Js, AWS Lambda How can I get nested asynchronous methods variables - javascript

I have written this lambda function in node js 8.10. This is not assign the value properly in the asynchronous1 method
var response;
var client = new AWS.CloudWatchLogs();
var params = {logGroupName: group};
client.describeMetricFilters(params, function(err, data) {
if(err) { console.log(err);}
else{
for(let p of data.metricFilters){
var patterns = [/\$\.errorCode\s*=\s*\"?\*UnauthorizedOperation(\"|\)|\s)/i, /\$\.errorCode\s*=\s*\"?AccessDenied\*(\"|\)|\s)/i];
if(findinString(patterns, p.filterPattern)){
var cwclient = new AWS.CloudWatch();
var response;
var par = {MetricName: p.metricTransformations[0].metricName, Namespace: p.metricTransformations[0].metricNamespace };
cwclient.describeAlarmsForMetric(function(par,data){
if(err){console.log(err);}
else{ response = data; }
});
var snsClient = new AWS.SNS();
var subscribers;
var yh = { TopicArn: response.MetricAlarms[0].AlarmActions[0] };
snsClient.listSubscriptionsByTopic(yh, function(err, data){
if(err){console.log(err);}
else {subscribers = data;}
});
if(subscribers.Subscriptions.length == 0){
result = true;
}
now in the above mthod first asynchronous method describeMetricFilters inside it there are 2 asynchronous method describeAlarmsForMetric and listSubscriptionsByTopic ...But I ca nnot get the response value which is set in the inside of the async method ...
cwclient.describeAlarmsForMetric(function(par,data){
if(err){console.log(err);}
else{ response = data; }
});
it did not set the value...when I trying to access `response from outside asynchronous method after setting the value I can not see any value stored in it...
Also same thing happen for here
if(subscribers.Subscriptions.length == 0) subscribers value can not display always return udefined...
So How can I handle this...Should I make await for these asynchronous methods....I am new on node js so any suggestion would be great for me.....
Basically how the variables which are assigned values inside of an async method would accessible outside of the async method. Or is there any better way to handle this??

Related

Use of indexedDB returns 'undefined'

I'm trying to use indexedDB.
Some parts of my code works.
In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.
The second function puts records in an array, there is also an undefined message not related to any line.
If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.
var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);
openDBforCreation.onupgradeneeded = function(e) {
var db = e.target.result;
var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias"
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};
function addServerInDB(serverAlias,serverAddress,user,pwd){
var myDB = indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var db = e.target.result,
request = db.transaction([dbServersStoreName],
"readwrite").objectStore("servers")
.put({alias:''+serverAlias+'',
address:''+serverAddress+'', login:''+user+'',
passwd:''+pwd+''});
request.onsuccess = function(){
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Server added'});
}
}
};
function listServersInDB(){
var myDB= indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var servers = new Array(),
db = e.target.result,
request = db.transaction(["servers"], "readwrite")
.objectStore("servers")
.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
servers.push(cursor.value);
cursor.continue();
}
return servers;
}
}
};
I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.
You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.
Briefly, don't do this:
function open() {
var openDatabaseRequest = ...;
}
openDatabaseRequest.foo = ...;
Instead, do this:
function open() {
var openDatabaseRequest = ...;
openDatabaseRequest.foo = ...;
}
Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.
Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.
Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:
function listServers(db, callbackFunction) {
var servers = [];
var tx = db.transaction(...);
var store = tx.objectStore(...);
var request = store.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if(cursor) {
servers.push(cursor.value);
cursor.continue();
}
};
tx.oncomplete = function() {
callbackFunction(servers);
};
return 'Requested servers to be loaded ... eventually callback will happen';
}
function connectAndList() {
var request = indexedDB.open(...);
request.onsuccess = function() {
var db = request.result;
listServers(db, onServersListed);
};
}
function onServersListed(servers) {
console.log('Loaded servers array from db:', servers);
}
When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.
When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.
If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.
async function getServers() {
var db = await new Promise(resolve => {
var request = indexedDB.open(...);
request.onsuccess = () => resolve(request.result);
});
var servers = await new Promise(resolve => {
var tx = db.transaction(...);
var request = tx.objectStore(...).getAll();
request.onsuccess = () => resolve(request.result);
});
return servers;
}
One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.
Clear and helpfull explanations, Thank you.
I open database multiple times beacause the first time is for checking if DB needs an upgrade and doing something if needed. I'll add 'db.close()' in each functions.
Then, I tried your exemple and the result is the same:
console.log('Loaded servers array from db:', servers); works
but return servers; Don't work.
And in console there is already an undefined without related line :
Screenshot

Firebase value is undefined when it is not supposed to be

I am working on a firebase project. During testing the
return user.val().name;
will return an
undefined
value however the
console.log(user.val().name)
will return the actual string stored in the .name field. Why is that. Also even if assign the
user.val().name
to a variable, the variable remains undefined.Please help figure out why this happens. I am printing it to a csv.
Here is my code:
var database = firebase.database();
var ref2 = database.ref('information/');
var id;
var name;
ref2.on("value", function (one) {
one.forEach(function (two) {
if (typeof two.val().Id !== 'undefined') {
id = two.val().Id;
name = program(id); //name undefined
}
else {
id = "";
}
csv = name + "," + id +"\n";
});
download(csv);
});
};
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name); //this doesnt show undefined
return user.val().name; //this shows undefined when appended to a html element
}
})
}
Note: In the firebase database, the name value is not null. It has a string added to it.
I second with Frank's reason on why your function program() doesn't work. Because ref.on('value'... makes an asynchronous call, program() does not wait for the completion of ref.on and exists with an undefined return value.
What you could instead do is use Promises. Wrap the statements inside your program() function within a Promise, and upon completion of the asynchronous call, resolve or reject based on the result it gives.
Here's your function with Promises:
function program(id) {
return new Promise(function (resolve, reject) {
try {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value', function (user) {
if (typeof user.val().name === 'undefined') {
resolve(null);
} else {
console.log(user.val().name);
resolve(user.val().name);
}
})
} catch (e) {
reject(e)
}
});
}
And then, here's how you can read the result:
program(id).then(function (result) {
console.log(result)
//Do what you want with the result here
}).catch(function (error) {
console.log(error)
})
Note: You're executing this block in a for-each statement. If you're using Promises, you'd also need to look into how to use Promises inside a loop. For reference, check Promise.all()
Most likely you are trying to use the returned name in the code that calls your program function. E.g.
var name = program("1234");
console.log(name); // this will print undefined
This will not work, since your program() is not actually returning name. Data is loaded from Firebase asynchronously. By the time program() exits, the data isn't loaded yet.
This is easiest to see by putting a few log statements into the code:
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
console.log("Before attaching listener");
ref.on('value',function(user){
console.log("Got value from database");
})
console.log("After attaching listener, exiting function");
}
This will print:
Before attaching listener
After attaching listener, exiting function
Got value from database
This is likely not the order that you expected, but it is working as designed. Instead of waiting for the data to be loaded (and making the browser/user wait), your code continues. Then when the data is loaded, your callback is invoked. But in your original code that means that your return statement is unable to return the name to the original caller.
This is precisely the reason why the Firebase Database (and most web APIs) use callbacks like the one you pass into on(). This callback is invoked when the data is available and is the only place where you can access the user data. So any code that requires the data you just loaded must be inside that callback, or be called from inside that callback. E.g.
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name);
appendToHtmlElement(user.val().name);
}
})
}
Firebase> DataBase> Role
Have you changed the value of rules?
Comment Example!
That can get the value after moving to name.
var ref = database.ref ("users/" + id + "/name");
ref.on ('value', function (user) {
    if (user.val ()! = null) {
         console.log (user.val ())
     }
}
If not, let's worry about it.
You should receive the returned value.
var name = ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name); //this doesnt show undefined
return user.val().name; //this shows undefined when appended to a html element
}
})
then use return name to get the value. or simply return the return ref.on('value' ...

How to await for async database call to resolve before proceeding with function to alter the DOM?

I'm trying to dynamically add elements to a Table, kind of like a ledger. I have a few HTML buttons. The buttons are loaded to the DOM dynamically from a MongoDB collection. I am using NodeJS, using PUG/Jade as the view engine, and I'm putting the _id values from the DB as the id values of each button. This way I can query the DB for other properties with an onclick function.
Each time I click a button, I run a fetch call to my backend get the price property stored in the MongoDB:
function getPriceFromDatabase() {
var target = event.target;
var id = target.id;
var url = '/menus/' + id;
fetch(url, {
method: 'GET'
}).then(function(res){
if(!res.ok) console.log("Error");
else return res.json().then(function(result){
var returnedItem = JSON.parse(result)
console.log("Item Price: " + returnedItem[0].itemPrice);
return returnedItem[0].itemPrice;
}).catch(function(err){
console.log(err);
});
});
}
This works fine.
My problem is when I try to call this function to add new rows to a table.
function createTableElement () {
var newTableRow = document.createElement("tr");
var target = event.target;
var nameCell = document.createElement("td");
var nameText = document.createTextNode(target.textContent);
nameCell.appendChild(nameText);
newTableRow.appendChild(nameCell);
var priceOfItem = getPriceFromDatabase()
var priceCell = document.createElement("td");
var priceText = document.createTextNode(priceOfItem);
priceCell.appendChild(priceText);
newTableRow.appendChild(priceCell);
ledgerTable.appendChild(newTableRow);
}
The function runs without waiting for the result of getPriceFromDatabase()
This means that I get a table where the first column correctly states the name of the button which was clicked, but the price column is undefined. It simply takes some time for the database to be queried and return a value, and by the time it happens the function which calls the query has already completed.
What are my options? I'm still a novice to using promises and asynchronous functions. I understand that fetch returns a promise as a response object, which is why I can use .then()
I have tried changing my DOM-table function to something like:
getPriceFromDatabase().then( [construct the rest of the table elements] )
This is invalid since .then() is not a property of that function, since it doesn't return a promise. What can I do?
I have tried doing
async getPriceFromDatabase() {}, but it still gives me an error that I can't use .then()
How can I solve this? Do I have to use new Promise ()? I would appreciate if someone could point me into the right direction.
getPriceFromDatabase() is an asynchronous function. You need to return the promise from it and use .then() on that returned promise:
function getPriceFromDatabase() {
var target = event.target;
var id = target.id;
var url = '/menus/' + id;
return fetch(url, {
method: 'GET'
}).then(function(res){
if(!res.ok) console.log("Error");
else return res.json().then(function(result){
var returnedItem = JSON.parse(result)
console.log("Item Price: " + returnedItem[0].itemPrice);
return returnedItem[0].itemPrice;
}).catch(function(err){
console.log(err);
throw err;
});
});
}
function createTableElement () {
var newTableRow = document.createElement("tr");
var target = event.target;
var nameCell = document.createElement("td");
var nameText = document.createTextNode(target.textContent);
nameCell.appendChild(nameText);
newTableRow.appendChild(nameCell);
getPriceFromDatabase().then(function(priceOfItem) {
var priceCell = document.createElement("td");
var priceText = document.createTextNode(priceOfItem);
priceCell.appendChild(priceText);
newTableRow.appendChild(priceCell);
ledgerTable.appendChild(newTableRow);
});
}
Summary of changes:
fetch(url) was changed to return fetch(url), thus returning the promise.
var priceOfItem = getPriceFromDatabase() is changed to getPriceFromDatabase.then(...).
Also, you really should be passing the event into both functions rather than relying on a global.
I have tried changing my DOM-table function to something like: getPriceFromDatabase().then( [construct the rest of the table elements] ).
That didn't work for you because you did not return the promise from getPriceFromDatabase() so therefore there was no .then() handler on the return value.
I tried async getPriceFromDatabase() {}, but it still gives me an error that I can't use .then()
Same issue. You still have to return the promise.

Javascript Promise().then to prevent re-calling the function before the first call be executed

In my node.js app, reading data from MSSQL using tedious, I'm calling the below every 1 second:
Fetch the data from the server (fetchStock function) and save it in temporary array
Send the data saved in the temporary array to the client using the Server-Sent Events (SSE) API.
It looks the 1 second is not enough to recall the fetchStock function before the previous call is completely executed, so I get execution errors from time to time.
I increased it to 5 seconds, but still get the same issue every once in a while.
How can I use Promise().then to be sure the fetchStock function is not re-called before the previouse call be completely executed?
var Request = require('tedious').Request;
var Connection = require('tedious').Connection;
var config = {
userName: 'sa',
password: 'pswd',
server: 'xx.xxx.xx.xxx',
options: {
database: 'DB',
rowCollectionOnRequestCompletion: 'true',
rowCollectionOnDone: 'true'
},
};
var sql = new Connection(config);
var addElem = (obj, elem)=> [].push.call(obj, elem);
var result = {}, tmpCol = {}, tmpRow = {};
module.exports = {
displayStock: function (es) {
var dloop = setInterval(function() {
if(result.error !== null)
if (es) es.send(JSON.stringify(result), {event: 'rmSoH', id: (new Date()).toLocaleTimeString()});
if(result.error === null)
if (es) es.send('connection is closed');
}, 1000);
},
fetchStock: function () {
request = new Request("SELECT ItemCode, WhsCode, OnHand FROM OITW where OnHand > 0 and (WhsCode ='RM' or WhsCode ='FG');", function(err, rowCount, rows) {
if (err) {
result = {'error': err};
console.log((new Date()).toLocaleTimeString()+' err : '+err);
}
if(rows)
rows.forEach(function(row){
row.forEach(function(column){
var colName = column.metadata.colName;
var value = column.value;
addElem(tmpCol, {colName: value})
});
addElem(tmpRow,{'item': tmpCol[0].colName, 'Whs': tmpCol[1].colName, 'Qty': tmpCol[2].colName});
tmpCol = {};
});
result = tmpRow;
tmpRow={}
});
sql.execSql(request);
}
}
I think what you need is a simple variable to check if there's already running request not Promise.
var latch = false;
// It will be called only if the previous call is completed
var doFetchStock = () => sql.execSql(new Request("SQL", (err, rowCount, rows) => {
// Your logic dealing with result
// Initializes the latch
latch = false;
});
module.exports = {
fetchStock: function () {
// Check if the previous request is completed or not
if (!latch) {
// Sets the latch
latch = true;
// Fetches stock
doFetchStock();
}
}
};
Actually I've used this kind of pattern a lot to allow some behavior only once.
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L397-L413
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L775-L797
Since javascript is mono-threaded a simple code like this should be enough on client-side
function () {
if(currentPromise != null){ // define in a closure outside
currentPromise = [..] // call to server which return a promise
currentPromise.then(function(){
currentPromise = null;
});
}
}

Express.js - foreach array of data and store them in the database

I'm using expressjs, bookshelf.js and I want to post array of data, foreach such data and save it.
I'm not exactly sure where the issue is (express, bookshelf or just plain old javascript), but here's the scenario: when I post the said array and try to iterate through it, I get the correct amount of saves into the database, but all have the value of the last item in array.
Here's the code:
router.post('/save/', function(req, res){
var data = req.body;
var result = [];
for (var i in data) {
var d = data[i];
if (d.user_id == -1) d.user_id = null;
new Term().where({'date_of_driving': d.day})
.fetch()
.then(function(terms){
if (terms != null) {
return new Term({'id':terms.id}).save(d).then(function(item_updated){});
} else {
return new Term().save(d).then(function(item_saved){});
}
})
.catch(function(error){
console.log(error);
});
}
res.send({'saved': 'ok'});
});
My understanding is, that these calls are asynchronous and always operate on the last data, because for sentence is quicker than save. Am I onto something?
What are some of the best, easiest and most correct solutions to this problem?
It's not clear from your code whether you want to the queries to be run in parallel or sequentially.
I am assuming parallel, but you can replace .map with .each and it will run sequentially
router.post('/save/', function(req, res){
Promise.map(req.body, function(d) {
if (d.user_id == -1) d.user_id = null;
return new Term().where({'date_of_driving': d.day}).fetch().then(function(terms){
if (terms != null) {
return new Term({'id':terms.id}).save(d);
} else {
return new Term().save(d);
}
});
}).then(function() {
res.send({'saved': 'ok'});
}).catch(Promise.OperationalError, function(e) {
// Note that stack reveals internal server code so you might
// not want to send it over in production
res.status(400).send({stack: e.stack, message: e.message});
});
});
There is no need to catch an error only to log it so I removed it. Only catch an error when you can handle it.
Essentially, the 'then' function will be executed asynchronously, and the loop will continue its execution independently, as you suspected.
I believe one solution is to define a function that will essentially take the parameter d as a reference and execute the asynchronous stuff with that. So this should work:
router.post('/save/', function(req, res){
var data = req.body;
var result = [];
for (var i in data) {
var d = data[i];
if (d.user_id == -1) d.user_id = null;
doTheThing(d);
}
res.send({'saved': 'ok'});
});
function doTheThing(d) {
new Term().where({'date_of_driving': d.day})
.fetch()
.then(function(terms){
if (terms != null) {
return new Term({'id':terms.id}).save(d).then(function(item_updated){});
} else {
return new Term().save(d).then(function(item_saved){});
}
})
.catch(function(error){
console.log(error);
});
}

Categories