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}`);
Related
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.
})());
});
Working on the IndexedDB API, I'm creating many objectStores that belong to the same database, in one transaction, when the user loads a webpage.
I order to do so, I created an object which contains many objectStores to be created, each one has it's name, data and index.
Then a function runs the object and effectively creates Database, objectStores and indexes for each one.
However of all OS's created, just the last member of the object gets populated. Say of 5 objects to be created and populated, 5 are created but only the last one is populated.
Clearly is a problem of overwriting or some issue related to the JS stack or asynchronicity.
I appreciate any help to make the code populate all OS not the last one.
My browser is Chrome 56, I fetch data from an API whose response is OK, and I'm coding on vanillajs. I appreciate your help in vanillajs, there is no way to use any library or framework different from what the modern Web Platform offers.
Here is the code:
On the HTML side, this is an example of the object:
var datastores = [{osName:'items', osEndpoint: '/api/data/os/1/1', osIndex:'value'}, {osName:'categories', osEndpoint: '/api/data/os/2/1', osIndex: 'idc'}];
On javascript:
var request = indexedDB.open(DB_NAME, DB_VERSION); // open database.
request.onerror = function (e) { // error callback
console.error("error: " + e.target.errorCode);
};
request.onupgradeneeded = function (e) { // the onupgradeneeded event which creates all schema, dataabase, objectstores and populates OS.
var db = this.result;
for (var i in datastores) { // loop the objectStore object.
var objectStore = db.createObjectStore(datastores[i].osName, {keyPath: "id"});
TB_NAME = datastores[i].osName; // instantiate each objectStore name.
objectStore.createIndex(datastores[i].osIndex, datastores[i].osIndex, { unique: false }); // create each index.
objectStore.transaction.oncomplete = function(e) { // oncomplete event, after creating OS...
fetchGet(datastores[i].osEndpoint, popTable); // runs a function to fetch from a designated endpoint and calls a function.
};
}
}
Now the functions: to fetch data and to populate data:
function fetchGet(url, function) { // fetch from API.
fetch(url, {
method: 'GET'
}).then(function(response) {
return response.json();
}).then(function(json) {
popTable (json);
}).catch(function(err) {
console.log('error!', err);
});
}
function popTable(json) {
var m = 0;
var tx = db.transaction(TB_NAME, "readwrite");
tx.oncomplete = function(e) {
console.log("Completed Transaction " + TB_NAME);
};
tx.onerror = function(e) {
console.error("error: " + e.target.errorCode);
};
var txObjectStore = tx.objectStore(TB_NAME);
for (m in json) {
var request = txObjectStore.add(json[m]);
request.onsuccess = function (e) {
console.log('adding... ' );
};
}
}
The for (var i in datastores) loop runs synchronously, updating the global TB_NAME variable every time. When the loop finishes, TB_NAME will be holding the name of the last object store.
By the time the asynchronous popTable calls run, TB_NAME will forever be holding the name of the last store, so that's the only one that will update. Try adding logging to popTable to see this.
You'll need to pass the current value of the store name along somehow (e.g. as an argument to fetchGet). Also note that although you pass popTable as a parameter when calling fetchGet you're not actually accepting it as an argument.
...
Specific changes:
Change how you call fetchGet to include the store name:
fetchGet(datastores[i].osEndpoint, popTable, datastores[i].osName);
Change the fetchGet function to accept the args:
function fetchGet(url, func, name) {
And then instead of calling popTable directly, do:
func(json, name);
And then change the definition of popTable to be:
function popTable(json, name) {
... and use name in the transaction.
Here is what I actually try to do in a node.js and socket.io server:
var Q = require('q');
module.exports.pushMyData = function (socket, RawData, row_id) {
return Q.fcall(function () {
layout.row_id = row_id;
}).then(function (resultData) {
socket.emit('resultData', resultData);
console.log("resultData emited ++++++:" + resultData);
});
};
In the scope of the module.exports.pushMyData function I can proof that all parameters deliver correct values and socket is ok, too.
The other params look like this:
rawData = {"myset":{"id":"22","item":"150"},
"myOtherset":{"hash":"55","branch":"150"},
"myid":"15"}
row_id = 234
Now I want to put the row_id simply into the RawData:
it should after that be emitted via socket as the resultData like that:
resultData = {"my_set":{"id":"22","item":"150"},
"my_other_set":{"hash":"55","branch":"150"},
"my_id":"15",
"row_id":234} // as a key value pair
I am actually confused because I find no way to fill the rawData literal with the row_id key/value, so that I suspect it has a race condition issue.
might be that the socket.emit starts before row_id was added to rawData.
It's a miracle to me ;-)
My tryout with Q seems not to be the right way. Please help.
You shouldn't need any promises here. Just assign the property:
module.exports.pushMyData = function (socket, rawData, row_id) {
rawData.row_id = row_id;
socket.emit('resultData', rawData);
console.log("resultData emited ++++++:", rawData);
};
I am developing my app, and one of the features will be messaging within the application. What I did, is I've developed 'send message' window, where user can send message to other user. The logic behind it is as following:
1. User A sends message to User B.
2. Firebase creates following nodes in 'Messaging':
"Messaging"->"User A"->"User B"->"Date & Time"->"UserA: Message"
"Messaging"->"User B"->"User A"->"Date & Time"->"UserA: Message"
Here is the code that I am using for sending messages:
sendMsg: function(receiver, content) {
var user = Auth.getUser();
var sender = user.facebook.id;
var receiverId = receiver;
var receiverRef = $firebase(XXX.firebase.child("Messaging").child(receiverId).child(sender).child(Date()));
var senderRef = $firebase(XXX.firebase.child("Messaging").child(sender).child(receiverId).child(Date()));
receiverRef.$set(sender,content);
senderRef.$set(sender,content);
},
(picture 1 in imgur album)
At the moment, I am trying to read the messages from the database, and sort them in according to date. What I've accomplished so far, is that I have stored the content of "Messaging/UserA/" in form of an Object. The object could be seen in the picture I've attached (picture 2).
http://imgur.com/a/3zQ0o
Code for data receiving:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
My question is: how can I read the object's messages? I would like to sort the according to the date, get the message and get the Id of user who has sent the message.
Thank you so much!
You seem to be falling for the asynchronous loading trap when you're reading the messages:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
That return statement that you have in the Messages.on("value" callback doesn't return that value to anyone.
It's often a bit easier to see what is going on, if we split the callback off into a separate function:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var messagesObj = snapshot.val();
return messagesObj;
},
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
console.log('Before adding on-value listener');
Messages.on("value", onMessagesChanged);
console.log('After adding on-value listener');
}
If you run the snippet like this, you will see that the console logs:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
This is probably not what you expected and is caused by the fact that Firebase has to retrieve the messages from its servers, which could potentially take a long time. Instead of making the user wait, the browser continues executing the code and calls your so-called callback function whenever the data is available.
In the case of Firebase your function may actually be called many times, whenever a users changes or adds a message. So the output more likely will be:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
Inside on-value listener
Inside on-value listener
...
Because the callback function is triggered asynchronously, you cannot return a value to the original function from it. The simplest way to work around this problem is to perform the update of your screens inside the callback. So say you want to log the messages, you'd do:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var i = 0;
snapshot.forEach(function(messageSnapshot) {
console.log((i++)+': '+messageSnapshot.val());
});
},
Note that this problem is the same no matter what API you use to access Firebase. But the different libraries handle it in different ways. For example: AngularFire shields you from a lot of these complexities, by notifying AngularJS of the data changes for you when it gets back.
Also see: Asynchronous access to an array in Firebase
I can return a value if I send a sync message:
// frame script
var chromeBtnText = sendSyncMessage("getChromeToolbarButtonText");
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
// chrome script
messageManager.addMessageListener("getChromeToolbarButtonText", listener);
function listener(message) {
return document.getElementById('myChromeToolbarButton').label.value;
}
How do I achieve this with a callback with sendAsyncMessage?
I was hoping to do something like:
// frame script
function myCallback(val) {
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
var chromeBtnText = sendAsyncMessage("getChromeToolbarButtonText", null, myCallback);
There is no callback for replies. In fact, there is no reply at all. The return value from the chrome message listener is simply ignored for async messages.
To do fully async communication, you'd have to send another message containing the reply.
Frame script
addMessageListener("getChromeToolbarButtonTextReply", function(message) {
alert(message.data.btnText);
});
sendAsyncMessage("getChromeToolbarButtonText");
Chrome
messageManager.addMessageListener("getChromeToolbarButtonText", function(message) {
var btnText = document.getElementById('myChromeToolbarButton').label.value;
// Only send message to the frame script/message manager
// that actually asked for it.
message.target.messageManager.sendAsyncMessage(
"getChromeToolbarButtonTextReply",
{btnText: btnText}
);
});
PS: All messages share a namespace. So to avoid conflicts when another piece of code wants to use the same name getChromeToolbarButtonText, you better choose a more unique name in the first place, like prefixing your messages with your add-on name my-unique-addoon:getChromeToolbarButtonText or something like that. ;)
I was also hoping to do something similar:
messageManager.sendAsyncMessage("my-addon-framescript-message", null, myCallback);
I'm going the other direction so the myCallback would be in chrome but it's exactly the same principle.
I'd used similar approaches to #Noitidart and #nmaier before but in this new case I wanted to bind to some local data so myCallback can behave differently based on the application state at the time the first message was sent rather than at the time the callback is executed, all while allowing for the possibility of multiple message round-trips being in progress concurrently.
Chrome:
let someLocalState = { "hello": "world" };
let callbackName = "my-addon-somethingUnique"; // based on current state or maybe generate a UUID
let myCallback = function(message) {
messageManager.removeMessageListener(callbackName, myCallback);
//message.data.foo == "bar"
//someLocalState.hello == "world"
}.bind(this); // .bind(this) is optional but useful if the local state is attached to the current object
messageManager.addMessageListener(callbackName, myCallback);
messageManager.sendAsyncMessage("my-addon-framescript-message", { callbackName: callbackName } );
Framescript:
let messageHandler = function(message) {
let responseData = { foo: "bar" };
sendAsyncMessage(message.data.callbackName, responseData);
};
addMessageListener("my-addon-framescript-message", messageHandler);
There's a real-world example here: https://github.com/luckyrat/KeeFox/commit/c50f99033d2d07068140438816f8cc5e5e290da9
It should be possible for Firefox to be improved to encapsulate this functionality in the built-in messageManager one day but in the mean-time this approach works well and with a surprisingly small amount of boiler-plate code.
in this snippet below. i add the callback before sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarbuttonText'... as i know it will send back. Then I remove it after callback executes. I know I don't have to but just to kind of make it act like real callback, just to kind of show people, maybe it helps someone understand.
Frame:
/////// frame script
function CALLBACK_getChromeToolbarButtonText(val) {
removeMessageListner('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //remove the callback
var chromeBtnText = val;
if (chromeBtnText == 'blah') {
alert('tool is blah');
}
}
addMessageListener('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage', CALLBACK_getChromeToolbarButtonText); //add the callback
var chromeBtnText = sendAsyncMessage("my-addon-id#jetpack:getChromeToolbarButtonText", null);
Chrome:
////// chrome script
messageManager.addMessageListener("my-addon-id#jetpack:getChromeToolbarButtonText", listener);
function listener() {
var val = document.getElementById('myChromeToolbarButton').label.value;
sendAsyncMessage('my-addon-id#jetpack:getChromeToolbarButtonTextCallbackMessage',val);
}