Nodejs MySql Global Variable Limitation - mysql

Why is the new Monsters object only being filled while being called inside the mysql function scope?
Monsters = {};
db.query('SELECT * from rpg_monsters ', function(err, results) {
for(i=0; i < results.length; i++){
Monsters[results[i].monster_id] = {
monster_id: results[i].monster_id,
monster_name: results[i].monster_name,
monster_filename: results[i].monster_filename,
monster_hp: results[i].monster_hp,
monster_chp: results[i].monster_hp,
monster_type: results[i].monster_type,
monster_level: results[i].monster_level,
xPosition: results[i].monster_xPosition,
yPosition: results[i].monster_yPosition
}
}
console.log(Monsters); // This Returns True with all the objects properties!
db.end();
});
console.log(Monsters); // This Returns Empty?
Is it possible use the Monsters object (or any) outside of the mysql callback?

Short answer: No.
Long answer: Welcome to asynchronous programming in NodeJS! Your error is not due to a scoping issue, but a time issue! Your asynchronous code does not run in source order. Your code does this:
initialize global variable Monsters reference an empty Javascript object
submit an asynchronous request to db.query, and pass a callback taking params err and results
console.log the contents of Monsters, which still references an empty array
NodeJS reaches the end of it's main loop and ticks. This may happen many times while waiting for db.query to finish its asynchronous IO. At some point in the future we resume:
db.query resolves and runs your callback function.
clone all elements in results into object at global variable Monsters
log value of Monsters
You will need to restructure your code to follow the asynchronous callback structure, or you can invesetigate alternatives like Promises or coroutines.
Please see How to return the response from an asynchronous call?, it has quite the explanation of your issue.

Related

How to put the results of MySQL database in a variable in Node.js?

I have a problem in my node.js application. I'm connecting to the database and getting the data
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
});
console.log(users[currentUser]);
When I try to console.log the result[0] from inside the function, it returns this:
RowDataPacket {
n: 11,
id: 'VKhDKmXF1s',
name: 'user3',
status: 'online',
socketID: 'JbZLNjKQK15ZkzTXAAAB',
level: 0,
xp: 0,
reg_date: 2018-07-16T20:37:45.000Z }
I want to put that result from MySQL into users.example or something like that, but when I try the code it returns undefined. So I tried console.log(users[currentUser].id) as an example and it shows an error
TypeError: Cannot read property 'id' of undefined
So how do I put the data from the result inside my variable users[currentUser]?
So how do I put the data from the result inside my variable users[currentUser]?
That's happening. The problem is how it is being tested.
The correct test is with the console.log inside the callback function(err, result)
function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Issue: users[currentUser] is still undefined outside that function
Well, yes and no. It is undefined in code that executes before the callback is fired.
And how do I fix that?
Anything that needs the result of the query must be executed from within the callback function, because that's the only code location where you know for certain that the data exists.
Well, I need that data outside in a global variable.
You can stick the query data in a global, but that doesn't solve the timing issue
of only accessing that global when it is defined and contains current data. That will cause lots of frustration.
If you don't want to call one or more specific functions to process the query data
within the callback, an alternative is to use a nodejs EventEmitter to coordinate the data production and data consumption.
Yet another alternative is to not use a callback function, and use Promise and/or async/await, both of which are supported by modern nodejs. This alternative doesn't involve global variables, but provides different ways to code the fact that some operations need to wait for the results of others.
connection.query is an async call. The console.log is called before the query fetches the data from db.
Because you're using a callback, your console.log is happening before the result comes back from the database. Additionally, you have a type in your code user -> users.
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Side note: research "SQL Injection" before you use this code. The way you're building your query opens you up for anyone to access your database. Using a library like squel.js or knex.js will help you avoid this.
For an explanation of why things happen in the order they do, take a look at the JavaScript event loop.

Handle Nested Queries Callback of MySQL in NodeJS

I am new to NodeJS with MySQL Database and I want to execute my code for nested queries.
The scenario is that I have to get the list of incomplete trades and then by using iterative loop to iterate the list which I have received from database.
In the loop there are more queries executing and a 3rd party API is called to fetch the data which returns data in a callback. Now the issue is that callback execute asynchronously and the loop doesnt wait for the callback to return data and it moves on.
Kindly guide me as I am stucked in this situation.
Here is my code
var sql = incompleteTradesQuery.getIncompleteTrades();
sqlConn.query(sql, function (err, data) {
if (err) {
console.log(err);
}
else {
for (var i = 0; i < data.length; i++) {
bittrexExchange.getOrder(data.order_uuid, function(err, order_data) {
if (order_data.result.IsOpen != true) {
var order_sql = tradesQuery.insertTrade(order_data.result.OrderUuid, order_data.result.Exchange, data.customer_id, order_data.result.Quantity, order_data.result.QuantityRemaining, order_data.result.Limit, order_data.result.Reserved, order_data.result.ReserveRemaining, order_data.result.CommissionReserved, order_data.result.CommissionReserveRemaining, order_data.result.CommissionPaid, order_data.result.Price, order_data.result.PricePerUnit, order_data.result.Opened, order_data.result.Closed, order_data.result.IsOpen, null, data.commission_fee, data.total_transfer, new Date());
sqlConn.query(order_sql);
var incomplete_trades_query = incompleteTradesQuery.deleteIncompleteTradesById(data.id);
sqlConn(incomplete_trades_query);
}
});
}
}
});
Since, NodeJS fundamentally works in asynchronous nature, your nested queries will also be asynchronous and it will be a painful task to write chain of callbacks. One simple answer to your question will be use promises.
Additionally, I would recommend you using asynchronous way to handle your multiple queries which will undoubtably, work faster than the synchronous way of handling queries/requests.
NodeJS also provides async.js module that will solve your problem. Q and Step are also good packages to handle your nested callback code.
https://code.tutsplus.com/tutorials/managing-the-asynchronous-nature-of-nodejs--net-36183

sails js assign rollback from .create() to variable

I know that it is trivial to many, but not for me because I am new to this. I need to assign the rollback function from create function to one variable I try:
var obj_transaction = Transaction.create({ amount: 200 }).exec(function(err, transaction){
return transaction;//if print transaction here returns json of new transaction
});
res.json(obj_transaction);//print undefined
return;
But this does not work, when I print obj_transaction it returns undefined, I need obj_transaction with info of new created transaction.
You can,t assign like this, because as create request make async call, the code of execution transfer to next line res.json before the callback function is called.
So res.json will be called before new data assign to variable. That is why, it gives u undefined.

How do I use the data returned by an ajax call?

I am trying to return an array of data inside a JSON object that is return from a URL, I can see the data that is being returned using console.log.
However when trying to catch the return array in a variable for example:
var arr = list();
console.log(arr.length);
The length being output by this code is "0" despite the fact that the data returned has content (so the length is greater than zero). How can I use the data?
list: function() {
var grades = [];
$.getJSON(
"https://api.mongolab.com/api/1/databases", function(data) {
console.log(data);
grades [0] = data[0].name;
console.log(grades.length);
});
return grades;
},
The issue you are facing is easy to get snagged on if you aren't used to the concept of asynchronous calls! Never fear, you'll get there.
What's happening is that when you call the AJAX, your code continues to process even though the request has not completed. This is because AJAX requests could take a long time (usually a few seconds) and if the browser had to sit and wait, the user would be staring in angsuish at a frozen screen.
So how do you use the result of your AJAX call?
Take a closer look at the getJSON documentation and you will see a few hints. Asynchronous functions like getJSON can be handled in two ways: Promises or Callbacks. They serve a very similar purpose because they are just two different ways to let you specify what to do once your AJAX is finished.
Callbacks let you pass in a function to getJSON. Your function will get called once the AJAX is finished. You're actually already using a callback in the example code you wrote, it's just that your callback is being defined inside of your list() method so it isn't very useful.
Promises let you pass in a function to the Promise returned by getJSON, which will get called once the AJAX is finished.
Since you are doing all this inside of a method, you have to decide which one you're going to support. You can either have your method take in callbacks (and pass them along) or you can have your method return the promise returned by getJSON. I suggest you do both!
Check it out:
var list = function(success) {
// Pass in the callback that was passed into this function. getJSON will call it when the data arrives.
var promise = $.getJSON("https://api.mongolab.com/api/1/databases", success)
// by returning the promise that getJSON provides, we allow the caller to specify the promise handlers
return promise;
}
// Using callbacks
list(function(grades) {
console.log(grades);
});
// Using promises
list()
.success(function(grades) {
console.log(grades);
});

How to save the result of d3.csv.parse to a global variable?

I have this
var result;
d3.csv("xxx.csv",function(data){
csvResultParser(data);
});
function csvResultParser(data){
//parse the data then assign it to result
}
But I still have result as "undefined", any clues?
The d3.csv() function is asynchronous. Thus, you have to wait for the data to be received before reading the result variable. This is the reason why, when dealing with asynchronous data, it is prefered to do everything inside the d3.csv() function instead of using global variables.