Socket.IO node js saving data to client - javascript

currently I want to save an int assigned from the server to my client
server side:
socket.emit('clientId', id);
Client Side:
var clientId;
socket.on('clientId', function(data){
clientId = data;
});
however, when I use console.log(clientId); outside the function, it becomes undefined. Is there a solution to this. Thanks

It's not that it becomes undefined outside of the function but it's most likely not defined yet. You didn't show how you want to access it outside of the function but if it's anything like this:
var clientId;
socket.on('clientId', function(data){
clientId = data;
});
console.log(clientId);
then the console.log line will be run before clientId = data so it is the same variable, it's just not defined yet.
You need to access that variable from your socket.on handler, or from some function that is run by that handler, or after that handler has already been run.
It's hard to tell anything more specific since you didn't say how do you want to access the variable and what do you want to do with it. You can see my example that I put on GitHub that does something like that to see if that could help you:
https://github.com/rsp/node-websocket-vs-socket.io/blob/master/si.html#L36-L46
It looks like this:
var l = document.getElementById('l');
var log = function (m) {
var i = document.createElement('li');
i.innerText = new Date().toISOString()+' '+m;
l.appendChild(i);
}
log('opening socket.io connection');
var s = io();
s.on('connect_error', function (m) { log("error"); });
s.on('connect', function (m) { log("socket.io connection open"); });
s.on('message', function (m) { log(m); });
Here, the log function is called from within the handler so that it is guaranteed to be called after the message has already been passed. The data is passed as an argument to that function but it could use a global variable as well.
Update
Here is a simpler example:
var clientId;
socket.on('clientId', function (data) {
clientId = data;
clientReady();
});
function clientReady() {
// you can access it here:
console.log(clientId);
}
Here you can access your variable in the clientReady function which is outside of the on handler. Think of it like you would about the window.onload handler:
window.onload = function() {
// ...
};
or $(document).ready() in jQuery:
$(document).ready(function () {
// ...
});
It is a piece of code that gets called on a certain moment to guarantee that everything that you need is ready.
Another example using a promise:
var clientIdPromise = new Promise(function (resolve, reject) {
socket.on('clientId', function (data) {
resolve(data);
});
});
// now everywhere in your code you can access it as:
clientIdPromise.then(function (clientId) {
// you have access to clientId here
});
Or shorter, using the fat arrow notation:
var clientIdPromise = new Promise(
(res, rej) => socket.on('clientId', data => res(data));
// and somewhere else:
clientIdPromise.then(clientId => {
// you have access to clientId here
});

JavaScript is async, it will only print the real value once the .on('clientId') event has been called. You should console.log it inside the function or after, but using EventEmitter.

Related

IndexedDB's callbacks not being executed inside the 'fetch' event of a Service Worker

I'm trying to do a couple of things in the IndexedDB database inside the 'fetch' event of a service worker, when the aplication asks the server for a new page. Here's what I'm going for:
Create a new object store (they need to be created dynamically, according to the data that 'fetch' picks up);
Store an element on the store.
Or, if the store already exists:
Get an element from the store;
Update the element and store it back on the store.
The problem is that the callbacks (onupgradeneeded, onsuccess, etc) never get executed.
I've been trying with the callbacks inside of each other, though I know that may not be the best approach. I've also tried placing an event.waitUntil() on 'fetch' but it didn't help.
The 'fetch' event, where the function registerPageAccess is called:
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
event.waitUntil(function () {
const nextPageURL = new URL(event.request.url);
if (event.request.destination == 'document') {
if (currentURL) {
registerPageAccess(currentURL, nextPageURL);
}
currentURL = nextPageURL;
}
}());
/*
* some other operations
*/
return response || fetch(event.request);
})
);
});
registerPageAccess, the function with the callbacks.
I know it's plenty of code, but just look at secondRequest.onupgradeneeded in the 5th line. It is never executed, let alone the following ones.
function registerPageAccess(currentPageURL, nextPageURL) {
var newVersion = parseInt(db.version) + 1;
var secondRequest = indexedDB.open(DB_NAME, newVersion);
secondRequest.onupgradeneeded = function (e) {
db = e.target.result;
db.createObjectStore(currentPageURL, { keyPath: "pageURL" });
var transaction = request.result.transaction([currentPageURL], 'readwrite');
var store = transaction.objectStore(currentPageURL);
var getRequest = store.get(nextPageURL);
getRequest.onsuccess = function (event) {
var obj = getRequest.result;
if (!obj) {
// Insert element into the database
console.debug('ServiceWorker: No matching object in the database');
const addRes = putInObjectStore(nextPageURL, 1, store);
addRes.onsuccess = function (event) {
console.debug('ServiceWorker: Element was successfully added in the Object Store');
}
addRes.onerror = function (event) {
console.error('ServiceWorker error adding element to the Object Store: ' + addRes.error);
}
}
else {
// Updating database element
const updRes = putInObjectStore(obj.pageURL, obj.nVisits + 1, store);
updRes.onsuccess = function (event) {
console.debug('ServiceWorker: Element was successfully updated in the Object Store');
}
updRes.onerror = function (event) {
console.error('ServiceWorker error updating element of the Object Store: ' + putRes.error);
}
}
};
};
secondRequest.onsuccess = function (e) {
console.log('ServiceWorker: secondRequest onsuccess');
};
secondRequest.onerror = function (e) {
console.error('ServiceWorker: error on the secondRequest.open: ' + secondRequest.error);
};
}
I need a way to perform the operations in registerPageAccess, which involve executing a couple of callbacks, but the browser seems to kill the Service Worker before they get to occur.
All asynchronous logic inside of a service worker needs to be promise-based. Because IndexedDB is callback-based, you're going to find yourself needing to wrap the relevant callbacks in a promise.
I'd strongly recommend not attempting to do this on your own, and instead using one of the following libraries, which are well-tested, efficient, and lightweight:
idb-keyval, if you're okay with a simple key-value store.
idb if you're need the full IndexedDB API.
I'd also recommend that you consider using the async/await syntax inside of your service worker's fetch handler, as it tends to make promise-based code more readable.
Put together, this would look roughly like:
self.addEventListener('fetch', (event) => {
event.waitUntil((async () => {
// Your IDB cleanup logic here.
// Basically, anything that can execute separately
// from response generation.
})());
event.respondWith((async () => {
// Your response generation logic here.
// Return a Response object at the end of the function.
})());
});

Save two clients data to a variable?

I have two clients who will be connecting to my server. I have the following code that sets up the server, and then clients would run the command
telnet localhost 3000 on their terminals. Now this part works
var http = require('http');
var net = require('net')
var listOfClients = []
var server = net.createServer(function(socket) {
socket.write("Welcome to ROCK PAPER SCISSORS choose from the following \n")
socket.write("[1] Rock \n")
socket.write("[2] Paper \n")
socket.write("[3] Scissors \n")
listOfClients.push(socket)
server.getConnections(function(error, count) {
if (count == 2) {
let p1 = listOfClients[0].on('data', function(data) {
return data;
});
let p2 = listOfClients[1].on('data', function(data) {
return data;
});
console.log(p1)
console.log(p2)
}
});
});
then the clients choose 1 or 2 or 3 for rock/paper/scissors I want to save what they used in a variable , but the method
let p1 = listOfClients[0].on('data', function(data) {
return data;
});
doesn't save the data into a variable and returns a lot of stuff that I don't understand. Any ideas on how to do this? I have the sockets in the list just need them save the clients input to a variable.
NodeJS works using events.
According to the documentations:
Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause Function objects ("listeners") to be called.
In your code, the listOfClients[0].on('data'... snippet of code, is actually creating a listener for the event 'data'.
In essence, you're telling the code to: Hey, can you keep listening to those and do something when it happens?
In your code, you're telling it to 'do something when the client[0] send some data'.
So when you write:
const variableName = something.on('someEvent', function(data) {});
The variable variableName is in reality, receiving the result of the event listener and using a callback as second argument.
Let's write a quick function that has one argument as a callback:
function myFunction(data, callback) {
callback("This is where you're trying to return the value");
return 'this is the event listener return';
}
const myVar = myFunction('Anything you please', function(callbackResult) {
console.log(`This is the callback: ${callbackResult}`);
});
console.log(`This is the var value: ${myVar}`);
Running the above code will output:
node v10.15.2 linux/amd64
This is the callback: This is where you're trying to return the value
This is the var value: this is the event listener return
One solution to your problem, is just assigning the data to a variable outside the event listener, like so:
const storeHere = [];
function myFunction(data, callback) {
callback("This is where you're trying to return the value");
return data;
}
const myVar = myFunction('Anything you please', function(callbackResult) {
storeHere.push(callbackResult); // Store somewhere outside
});
console.log(`This is the externalVar value: ${storeHere}`);
console.log(`This is the var value: ${myVar}`);

Trying to use a module pattern to safely get data

I'm attempting to not have to use a global variable called 'data'
In my js file I now have this function:
var Module_BarDataDaily = (function() {
var data;
d3.csv("myData.csv", function(rows) {
data = rows;
});
return {
dataX: data
}
})();
I was then (probably wrongly) under the impression that I can access this data via Module_BarDataDaily.dataX - so in a subsequent function I do the following:
function TOPbarChart(
grp, meas, colorChosen) {
TOPbarData = Module_BarDataDaily.dataX.map(function(d) { //line 900
return { ...
Console then just gives me an exception on line 900 of the following:
TypeError: Module_BarDataDaily.dataX is undefined
What am I doing wrong & how do I fix it?
The issue here is that d3.csv is asynchronous, so data is filled at a different time than accessed.
If you want to run d3.csv once, get the data and save them elsewhere, you can try something like this or this or this
In general:
// define your module here
var Module_BarDataDaily = function(data) {
this.dataX = data; //this.dataX will store the data
this.process = function(){// do whatever you need afterwards
console.log(this.dataX);
}
};
var process = function(myModule){
// or use another function to do what you need etc
console.log(myModule.dataX);
}
// load your csv once and save the data
d3.csv("path/to/your.csv", function(error, data) {
// after some proper error handling...
var myModule = new Module_BarDataDaily(data);
// so then you can do anything after that and
// your myModule will have data without calling d3.csv again
myModule.process();
//or
process(myModule);
});
Hope this helps!
Good luck!
I've used the following based on mkaran's answer:
var Module_BarDataDaily = function(data) {
this.dataX = data;
};
d3.csv("data/BarChart_data.csv", function(error, rows) {
var myModule = new Module_BarDataDaily(rows);
var chart = barChart();
chart.render(myModule.dataX);
d3.selectAll('input[name="meas"]').on("change", function change() {
chart.currentMeasure(this.value)
chart.render(myModule.dataX);
});
});

How do I mock functions in Mocha and Sinon with functions within a function

So for starters, I'm using coffeescript, but I'll add the JS code as well. I tried a bunch of different things but for the most part, everything ends up with Attempted to wrap undefined property undefined as function or something similar. In the case of a fake server, I got back 0 results when I expected, say 20.
// As JS
downloadResults = (function(_this) {
return function(q) {
var conn;
conn = new s.AjaxRequest(queryString);
conn.success = function(data) {
return downloadResultsSuccess(data, q);
};
conn.error = function(data) {
return downloadResultsError(data, q);
};
conn.post();
return true;
};
})(this);
# As coffee
downloadResults = (q) =>
conn = new s.AjaxRequest queryString
conn.success = (data) => downloadResultsSuccess data, q
conn.error = (data) => downloadResultsError data, q
conn.post()
true
OK, so what do I want to test? Well for starters, I want to mock the s.AjaxRequest bit so that when conn.post() is called, success is returned with the fake data. Next, I want to make sure that the function downloadResultsSuccess is called if success else downloadResultsError is called. And finally ideally, I want to make validate what data has.
Here are some things I've tried:
sinon.stub(s.AjaxRequest, 'post').yieldsTo('success', fakeData)
sinon.stub(s.AjaxRequest,'post')
.withArgs('/fake/server/data.json')
.returns({
post: function() {
return $.promise().done(fakeData);
}
});
server = sinon.fakeServer.create();
server.respondWith('POST', '/fake/server/data.json', [
200, {
'Content-Type': 'application/json'
}, fakeData
]);
Any other ideas on what I should do next?

Struggling with Q.js promises

I am sure I am missing something obvious but I can't seem to make heads or tails of this problem. I have a web page that is being driven by javascript. The bindings are being provided by Knockout.js, the data is coming down from the server using Breeze.js, I am using modules tied together with Require.js. My goal is to load the html, load the info from Breeze.js, and then apply the bindings to show the data to the user. All of these things appear to be happening correctly, just not in the correct order which is leading to weird binding errors. Now on to the code.
I have a function that gets called after the page loads
function applyViewModel() {
var vm = viewModel();
vm.activate()
.then(
applyBindings(vm)
);
}
This should call activate, wait for activate to finish, then apply bindings....but it appears to be calling activate, not waiting for it to finish and then runs applybindings.
activate -
function activate() {
logger.log('Frames Admin View Activated', null, 'frames', false);
return datacontext.getAllManufacturers(manufacturers)
.then(function () {
manufacturer(manufacturers()[0]);
}).then(function () {
datacontext.getModelsWithSizes(modelsWithSizes, manufacturers()[0].manufacturerID())
.then(datacontext.getTypes(types));
});
}
datacontext.getAllManufacturers -
var getAllManufacturers = function (manufacturerObservable) {
var query = entityQuery.from('Manufacturers')
.orderBy('name');
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
if (manufacturerObservable) {
manufacturerObservable(data.results);
}
log('Retrieved [All Manufacturer] from remote data source',
data, true);
}
};
datacontext.getModelsWithSizes -
var getModelsWithSizes = function (modelsObservable, manufacturerId) {
var query = entityQuery.from('Models').where('manufactuerID', '==', manufacturerId)
.orderBy('name');
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
if (modelsObservable) {
for (var i = 0; i < data.results.length; i++) {
datacontext.getSizes(data.results[i].sizes, data.results[i].modelID());
// add new size function
data.results[i].addNewSize = function () {
var newValue = createNewSize(this.modelID());
this.sizes.valueHasMutated();
return newValue;
};
}
modelsObservable(data.results);
}
log('Retrieved [Models With Sizes] from remote data source',
data, false);
}
};
Any help on why this promise isn't working would be appreciated, as would any process to figure it out so I can help myself the next time I run into this.
A common mistake when working with promises is instead of specifying a callback, you specify the value returned from a callback:
function applyViewModel() {
var vm = viewModel();
vm.activate()
.then( applyBindings(vm) );
}
Note that when the callback returns a regular truthy value (number, object, string), this should cause an exception. However, if the callback doesn't return anything or it returns a function, this can be tricky to locate.
To correct code should look like this:
function applyViewModel() {
var vm = viewModel();
vm.activate()
.then(function() {
applyBindings(vm);
});
}

Categories