Socket.io - Variables Not Updating Outside socket.on - javascript

I am working with Socket.io and MongoDB. When I first send my socket.emit to the server, the server takes in the parameter and returns with my desired output. When I reach the socket.on on my client.js the OrderID is shown to the console. Yet when I exit the socket.on method block, the GenOrderID variable becomes undefined.
My question is: why are variables that were created before the socket.on, no longer accessible outside it.
Here is the client.js I am working with:
// Create order array
var Order = [];
// Create GeneratedOrderID variable
var GenOrderID;
// Get School ID from cookie
var SchoolID = getCookie("SchID");
// Generate OrderID
socket.emit('GenerateOrderID', SchoolID);
socket.on('GenerateOrderID', function(GeneratedOrderID) {
console.log("OrderID sent from server is: " + GeneratedOrderID); // This returns the desired number
GenOrderID = GeneratedOrderID;
});
Order[0] = GenOrderID;
console.log("GenOrderID is: " + GenOrderID); // This returns undefined
console.log("Order[0] is: " + Order[0]); // This returns undefined
Here is the server.js I am working with:
socket.on('GenerateOrderID', function(PassSchoolID) {
// Connect to database
MongoClient.connect('mongodb://localhost:27017/Orders', function(err, db) {
// Handle errors
assert.equal(null, err);
console.log("Begin creation of OrderID");
db.collection('Orders').find({SchoolID: PassSchoolID}).sort({amount: -1}).limit(1).forEach(function (result) {
var GeneratedOrderID = parseInt(result.OrderID);
GeneratedOrderID++;
console.log("The server says the OrderID is: " + GeneratedOrderID); // This returns the desired number
// Return to client
socket.emit('GenerateOrderID', GeneratedOrderID);
});
});
});

By placing the console.log() inside the socket.on I was able to have it work properly.
// Create order array var
Order = [];
// Create GeneratedOrderID variable
var GenOrderID;
// Get School ID from cookie
var SchoolID = getCookie("SchID");
// Generate OrderID
socket.emit('GenerateOrderID', SchoolID);
socket.on('GenerateOrderID', function(GeneratedOrderID) {
console.log("OrderID sent from server is: " + GeneratedOrderID);
GenOrderID = GeneratedOrderID;
Order[0] = GenOrderID;
console.log("GenOrderID is: " + GenOrderID);
console.log("Order[0] is: " + Order[0]);
});

You're setting the value of GenOrderID inside a callback function, which is only executed once a GenerateOrderId event has occurred. The code inside of a callback function doesn't run until that function is called.
For example:
function run(f) {
// call `f` after 1 second
setTimeout(f, 1000);
};
var foo;
run(function() {
foo = 'bar';
console.log('in callback:', foo);
});
console.log('before callback:', foo);
// output:
// before callback: undefined
// in callback: 'bar'
At some point in the future, foo will equal 'bar', but you can only know this is the case after your callback has been called. And you can only know that your callback has been called from inside it.

Related

Why do I get an error when putting a JSONObject into an indexedDB database?

I am currently trying to get an indexedDB database running. However, I am struggling with some issues regarding indexedDB's put method. Although the keypath is defined and the JSONObject that is handed over contains a value which is named in the same way as the defined keypath, the put method causes the following error:
Uncaught DOMException: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
In order to make sure that the JSONObject really contains the value that shall be used as the key, I am logging the object. Thats what it looks like:
{"key":102019,"month":10,"year":2019,"spendings":[{"type":{"name":"Technology","importance":70,"iconURL":"./Resources/Technology.png"},"cost":"1500","name":"Macbook pro","timestamp":1571696285911}],"budget":0}
The code that is being used to store the data is the following:
function callbackSaveSpendingMonth(database, spendingMonth) {
let userName = defaultUserName;
let transaction = database.transaction(userName, "readwrite");
let objectStore = transaction.objectStore(userName, { keyPath: 'key' });
let JSONspendingMonth = JSON.stringify(spendingMonth);
console.log(JSONspendingMonth);
let request = objectStore.put(JSONspendingMonth);
request.onsuccess = function (event) {
console.log("The month " + spendingMonth.getMonth() + "/" + spendingMonth.getYear() + " has been saved successfully!");
}
transaction.oncomplete = function (event) {
console.log("A connection to indexedDB has successfully been established!");
}
}

Node JS ignores undefined check

Im working on a NodeJs app that takes an event from FB and puts it into a local database. For every event in the first page of the api query this goes well, except for the last one.
I am getting the following error:
[December 1st 2016, 1:48:39 pm] TypeError: Cannot read property 'name'
of undefined
at IncomingMessage. (/home/node/virgo/app.js:217:32)
at IncomingMessage.emit (events.js:129:20)
at _stream_readable.js:907:16
at process._tickCallback (node.js:372:11)
Just after
console.log(resultBody);
Code:
function addFBEvent(facebookId){
console.log("getting event: " + facebookId);
var options = {
hostname: 'graph.facebook.com',
port: 443,
path: '/v2.8/'+facebookId+'?fields=name,description,start_time,end_time,place,photos{images}&access_token={INSERT API ACCESS CODE HERE}',
method: 'GET'
};
https.request(options, function(res2) {
var resultBody = "";
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
resultBody = resultBody + chunk;
});
res2.on('end', function () {
dbConnection = sql.createConnection({
host : settings.dbHost,
user : settings.dbUser,
password : settings.dbPassword
});
dbConnection.connect(function(err){
if(!err) {
console.log("Database is connected ... nn");
} else {
console.log("Error connecting database ... nn");
}
});
var json = JSON.parse(resultBody);
console.log(resultBody);
if (json != undefined){
var eventName = json.name;
var eventStart = json.start_time;
var eventEnd = json.end_time;
var eventDescription = json.description;
var eventPlace = json.place.name;
var eventPoster = json.photos.data[json.photos.data.length-1].images[0].source;
var eventId = json.id;
console.log("name: " + eventName + ", start: " + eventStart + ", end: " + eventEnd + ", place: " + eventPlace + ", Poster: " + eventPoster);
//console.log("Description: " + eventDescription);
dbConnection.query('INSERT INTO SVVirgo.activities(title, description, image, start, end, price, location, facebook) VALUES ("'+eventName+'","'+eventDescription+'","'+eventPoster+'","'+eventStart+'","'+eventEnd+'",0,"'+eventPlace+'","'+eventId+'")', function (err, result){
});
}
dbConnection.end();
})
}).end();
}
See graph api explorer for the breaking event: Graph API
Event code: 1682486658666506
I've tried catching the undefined json object with no luck, i've also tried to validate the json using this solution: Stackoverflow validate JSON javascript
Instead of https.request try using request.
It will give you parsed JSON and you won't have to do it manually.
If you want to do it manually like you do, then remember to wrap var json = JSON.parse(resultBody); in a try/catch block (or use tryjson) because the JSON.parse cannot be used on unknown data outside of a try/catch - it can throw exceptions.
Another thing, don't open you database connection in your route handlers. You should open the connection once and just use it in your handlers. See this answer for more info about it.
Right now you are connecting to the database but you continue outside of the connection callback, so you run all the lines beginning from var json = JSON.parse(resultBody); before the DB connection is established.
Additionally, the error may be not because json is undefined but because json.place is undefined.
You can change this:
var eventPlace = json.place.name;
to this:
var eventPlace = json.place && json.place.name;
You must also check json.photos before you access json.photos.data and test if json.photos.data is an array before you treat it as such (you can do it with:
if (json.photos && Array.isArray(json.photos.data)) {
// ...
}
Basically, you need to make sure the values are what you want them to be before you access them. For example accessing this:
json.photos.data[json.photos.data.length-1].images[0].source
can fail when json is undefined, when json.photos is undefined, when json.photos.data is undefined, when json.photos.data is not an array, when json.photos.data.length is zero, when json.photos.data[json.photos.data.length-1] is undefined, when json.photos.data[json.photos.data.length-1].images is undefined, when json.photos.data[json.photos.data.length-1].images is not an array, when json.photos.data[json.photos.data.length-1].images is an array but it's empty, or when json.photos.data[json.photos.data.length-1].images is a non-empty array but json.photos.data[json.photos.data.length-1].images[0].source is undefined.
As you can see there are a lot of assumptions that you are doing here. When those assumptions are not met, the code will fail.

How to invoke C++/Qt function from Javascript in QWebengine?

I have looked the Qt example about QWebChannel .
But its a C/S app. BTW it doesn't work on my computer(OS X 10.10.5, Qt5.5).
All my HTML contents come from local disk. I don't think it needs socket things.
new QWebChannel(navigator.qtWebChannelTransport, function(channel) {
// all published objects are available in channel.objects under
// the identifier set in their attached WebChannel.id property
var foo = channel.objects.foo;
// access a property
alert(foo.hello);
// connect to a signal
foo.someSignal.connect(function(message) {
alert("Got signal: " + message);
});
// invoke a method, and receive the return value asynchronously
foo.someMethod("bar", function(ret) {
alert("Got return value: " + ret);
});
});
There is no
navigator.qtWebChannelTransport
such a thing, JS stopped there.
What should I fill with the first parameter ?
For QtWebEngine, you have to use qt.webChannelTransport.

how to do joins on Firebase tables

I am trying to get data from two tables like (fetch all users and their details)
tableOne.on('users', function (snapshot) {
userId = snapshot.val().userId; // line 1 (results like 1,2,3,4,5,6)
anotherTable.child('userdetails').child(userId).once('value', function(mediaSnap) {
// result // line 2
});
});
but the problem is line 1 executes first for the 6 times and then line 2 that n times resulting in everytime looking for 'where user id is - 6'...isn't joins supported in Firebase?
Any help is apreciated
Your code snippet has a nasty side-effect:
var userId;
tableOne.on('value', function (snapshot) {
userId = snapshot.val().userId; // line 1 (results like 1,2,3,4,5,6)
anotherTable.child('userdetails').child(userId).once('value', function(mediaSnap) {
console.log(userId + ":" + mediaSnap.val().name);
});
});
You're not declaring userId as a variable, which means that it becomes a global variable in JavaScript. And since the callback function executes asynchronously, there is a good chance that the global values will have changed by the time you need it.
The solution is simply to make userId a local variable of the callback function:
tableOne.on('value', function (snapshot) {
var userId = snapshot.val().userId; // line 1 (results like 1,2,3,4,5,6)
anotherTable.child('userdetails').child(userId).once('value', function(mediaSnap) {
console.log(userId + ":" + mediaSnap.val().name);
});
});
This will ensure that each value of userId is captured inside the function.
Solution for list join:
tableOne.orderByKey().on("value", function (snapshot) {
//console.log(snapshot.val());
snapshot.forEach(function (data) {
tableTwo.once('value').then(function (info) {
info = info.val();
});
});
});

Add new element in existing object

I am using node.js.
I have to add new elements in the object before to send a response to client.
user.getMatch(req.user, function(err, match){
for( k=0; k<match.length; k++){
var userId = {
id : match[k].match_id
};
var user = new User(userId);
console.log('k: ' + k);
user.getUserInfo(function(err2, info){
console.log('k here: ' + k);
if(info){
match[k].foo = info[0].foo;
}
});
}
var response = {
data : match
};
res.json(response);
});
I want to add an element "foo" from user.getUserInfo to the object "match" that was returned by user.getMatch. And then send all the data as response to the client.
But it got an error because "k" inside of user.getUserInfo is not equal to the "k" outside.
I do not know why the both "k" are not equal.
And how will I send a response to the client after performing the loop.
Thanks for your help!
Some problems here:
First, k is not defined so the k you're using is actually a global variable which is not what you want. You need to define it as 'var k'.
Second, the callback function you're passing to user.getUserInfo() is (probably) executed at some unknown time in the future. At this point your loop for (k ... has already finished so the the k variable already has a new value since the value that it had when you called user.getUserInfo(). And here's the tricky part: the code inside your callback function will use k's most recent value. It will not use the value that k had when the function was created.
You can solve this by adding a parameter to your callback function and binding k to it using the .bind method:
user.getMatch(req.user, function(err, match){
var k;
for(k=0; k < match.length; k++){
var userId = {
id : match[k].match_id
};
var user = new User(userId);
console.log('k: ' + k);
var callback = function(k, err2, info){
console.log('k here: ' + k);
if(info){
match[k].foo = info[0].foo;
}
}.bind(null, k);
user.getUserInfo(callback);
}
var response = {
data: match
};
res.json(response);
});
Also, you'd be better off by using .forEach for iterating over an array:
user.getMatch(req.user, function(err, match){
match.forEach(function(curr) {
var userId = {
id : curr.match_id
};
var user = new User(userId);
user.getUserInfo(function(err2, info){
if(info){
curr.foo = info[0].foo;
}
}
});
var response = {
data: match
};
res.json(response);
});
Although Array.forEach can give you your current index in the iteration, this is no longer needed. simply use the curr value (which gives you the current element in the iteration).
Finally, I think the code here is likely to send the response before all user.getUserInfo() calls have been executed. To achieve that you need to know when all user.getUserInfo() have been completed. This can be achieved by adding a variable numLeft which is decremented each time we get a user info. when this variable reaches zero we know that all getUserInfo() have completed and it is therefore safe to send the response back.
user.getMatch(req.user, function(err, match) {
var numLeft = match.length;
match.forEach(function(curr) {
var user = new User({
id : curr.match_id
});
user.getUserInfo(function(err2, info){
if(info) {
curr.foo = info[0].foo;
}
--numLeft;
if (numLeft == 0)
res.json({ data: match });
}
});
});
When you say "k inside and outside" do you mean inside and outside ofuser.getUserInfo(function(err2, info){})?
I am not sure of your context however i can think of two things
Since the function "function(err2, info)" is a callback and is executed asynchronously the context/stack in which k is used within getUserInfo is completely different. So try to pass k while calling i.e.
user.getUserInfo(function(err2, info, k){}). This should work
Try to declare k i.e var k in the closure that you want it to be used
Updating for another part of question
"But I got another issue.. it sends a response to client before it adds the "foo" element. So in the response to client, it only sends the object from "match" without the "foo" element."
That is again because ur code inside get user info gets executed asynchronously.
For this you need to keep a global flag or try to send the response from within getUserInfo
i.e.
var mathLen = match.length;
user.getUserInfo(function(err2, info,k,mathLen)
{
console.log('k here: ' + k);
if(info){
match[k].foo = info[0].foo;
}
if(k==mathLen)
{
var response = {
data : match
};
res.json(response);
}
});

Categories