WebSocket scaling on the client side with NodeJS - javascript

I have written the script below which creates multiple WebSocket connections with a smart contract to listen to events. it's working fine but I feel this is not an optimized solution and probably this could be done in a better way.
const main = async (PAIR_NAME, PAIR_ADDRESS_UNISWAP, PAIR_ADDRESS_SUSHISWAP) => {
const PairContractHTTPUniswap = new Blockchain.web3http.eth.Contract(
UniswapV2Pair.abi,
PAIR_ADDRESS_UNISWAP
);
const PairContractWSSUniswap = new Blockchain.web3ws.eth.Contract(
UniswapV2Pair.abi,
PAIR_ADDRESS_UNISWAP
);
const PairContractHTTPSushiswap = new Blockchain.web3http.eth.Contract(
UniswapV2Pair.abi,
PAIR_ADDRESS_SUSHISWAP
);
const PairContractWSSSushiswap = new Blockchain.web3ws.eth.Contract(
UniswapV2Pair.abi,
PAIR_ADDRESS_SUSHISWAP
);
var Price_Uniswap = await getReserves(PairContractHTTPUniswap);
var Price_Sushiswap = await getReserves(PairContractHTTPSushiswap);
// subscribe to Sync event of Pair
PairContractWSSUniswap.events.Sync({}).on("data", (data) => {
Price_Uniswap = (Big(data.returnValues.reserve0)).div(Big(data.returnValues.reserve1));
priceDifference(Price_Uniswap, Price_Sushiswap, PAIR_NAME);
});
PairContractWSSSushiswap.events.Sync({}).on("data", (data) => {
Price_Sushiswap = (Big(data.returnValues.reserve0)).div(Big(data.returnValues.reserve1));
priceDifference(Price_Uniswap, Price_Sushiswap, PAIR_NAME);
});
};
for (let i = 0; i < pairsArray.length; i++){
main(pairsArray[i].tokenPair, pairsArray[i].addressUniswap, pairsArray[i].addressSushiswap);
}
In the end, I instantiate the main function multiple times for each pair from a pair array, in a for-loop. I think this way of solving is brute force and there is a better way of doing this.
Any suggestions/opinions would be really appreciated.

Just to clear up the terms: You're opening a websocket connection to the WSS node provider - not to the smart contracts. But yes, your JS snippet subscribes to multiple channels (one for each contract) within this one connection (to the node provider).
You can collect event logs from multiple contracts through just one WSS channel using the web3.eth.subscribe("logs") function (docs), passing it the list of contract addresses as a param. Example:
const options = {
// list of contract addresses that you want to subscribe to their event logs
address: ["0x123", "0x456"]
};
web3.eth.subscribe("logs", options, (err, data) => {
console.log(data);
});
But it has a drawback - it doesn't decode the event log data for you. So your code will need to find the expected data types based on the event signature (returned in data.topics[0]). Once you know which event log is emitted based on the topics[0] event signature (real-life example value in this answer), you can use the decodeLog() function (docs) to get the decoded values.

Related

JavaScript function blocks web socket & causes sync issue & delay

I have a web socket that receives data from a web socket server every 100 to 200ms, ( I have tried both with a shared web worker as well as all in the main.js file),
When new JSON data arrives my main.js runs filter_json_run_all(json_data) which updates Tabulator.js & Dygraph.js Tables & Graphs with some custom color coding based on if values are increasing or decreasing
1) web socket json data ( every 100ms or less) -> 2) run function filter_json_run_all(json_data) (takes 150 to 200ms) -> 3) repeat 1 & 2 forever
Quickly the timestamp of the incoming json data gets delayed versus the actual time (json_time 15:30:12 vs actual time: 15:31:30) since the filter_json_run_all is causing a backlog in operations.
So it causes users on different PC's to have websocket sync issues, based on when they opened or refreshed the website.
This is only caused by the long filter_json_run_all() function, otherwise if all I did was console.log(json_data) they would be perfectly in sync.
Please I would be very very grateful if anyone has any ideas how I can prevent this sort of blocking / backlog of incoming JSON websocket data caused by a slow running javascript
function :)
I tried using a shared web worker which works but it doesn't get around the delay in main.js blocked by filter_json_run_all(), I dont thing I can put filter_json_run_all() since all the graph & table objects are defined in main & also I have callbacks for when I click on a table to update a value manually (Bi directional web socket)
If you have any ideas or tips at all I will be very grateful :)
worker.js:
const connectedPorts = [];
// Create socket instance.
var socket = new WebSocket(
'ws://'
+ 'ip:port'
+ '/ws/'
);
// Send initial package on open.
socket.addEventListener('open', () => {
const package = JSON.stringify({
"time": 123456,
"channel": "futures.tickers",
"event": "subscribe",
"payload": ["BTC_USD", "ETH_USD"]
});
socket.send(package);
});
// Send data from socket to all open tabs.
socket.addEventListener('message', ({ data }) => {
const package = JSON.parse(data);
connectedPorts.forEach(port => port.postMessage(package));
});
/**
* When a new thread is connected to the shared worker,
* start listening for messages from the new thread.
*/
self.addEventListener('connect', ({ ports }) => {
const port = ports[0];
// Add this new port to the list of connected ports.
connectedPorts.push(port);
/**
* Receive data from main thread and determine which
* actions it should take based on the received data.
*/
port.addEventListener('message', ({ data }) => {
const { action, value } = data;
// Send message to socket.
if (action === 'send') {
socket.send(JSON.stringify(value));
// Remove port from connected ports list.
} else if (action === 'unload') {
const index = connectedPorts.indexOf(port);
connectedPorts.splice(index, 1);
}
});
Main.js This is only part of filter_json_run_all which continues on for about 6 or 7 Tabulator & Dygraph objects. I wante to give an idea of some of the operations called with SetTimeout() etc
function filter_json_run_all(json_str){
const startTime = performance.now();
const data_in_array = json_str //JSON.parse(json_str.data);
// if ('DATETIME' in data_in_array){
// var milliseconds = (new Date()).getTime() - Date.parse(data_in_array['DATETIME']);
// console.log("milliseconds: " + milliseconds);
// }
if (summary in data_in_array){
if("DATETIME" in data_in_array){
var time_str = data_in_array["DATETIME"];
element_time.innerHTML = time_str;
}
// summary Data
const summary_array = data_in_array[summary];
var old_sum_arr_krw = [];
var old_sum_arr_irn = [];
var old_sum_arr_ntn = [];
var old_sum_arr_ccn = [];
var old_sum_arr_ihn = [];
var old_sum_arr_ppn = [];
var filtered_array_krw_summary = filterByProperty_summary(summary_array, "KWN")
old_sum_arr_krw.unshift(Table_summary_krw.getData());
Table_summary_krw.replaceData(filtered_array_krw_summary);
//Colour table
color_table(filtered_array_krw_summary, old_sum_arr_krw, Table_summary_krw);
var filtered_array_irn_summary = filterByProperty_summary(summary_array, "IRN")
old_sum_arr_irn.unshift(Table_summary_inr.getData());
Table_summary_inr.replaceData(filtered_array_irn_summary);
//Colour table
color_table(filtered_array_irn_summary, old_sum_arr_irn, Table_summary_inr);
var filtered_array_ntn_summary = filterByProperty_summary(summary_array, "NTN")
old_sum_arr_ntn.unshift(Table_summary_twd.getData());
Table_summary_twd.replaceData(filtered_array_ntn_summary);
//Colour table
color_table(filtered_array_ntn_summary, old_sum_arr_ntn, Table_summary_twd);
// remove formatting on fwds curves
setTimeout(() => {g_fwd_curve_krw.updateOptions({
'file': dataFwdKRW,
'labels': ['Time', 'Bid', 'Ask'],
strokeWidth: 1,
}); }, 200);
setTimeout(() => {g_fwd_curve_inr.updateOptions({
'file': dataFwdINR,
'labels': ['Time', 'Bid', 'Ask'],
strokeWidth: 1,
}); }, 200);
// remove_colors //([askTable_krw, askTable_inr, askTable_twd, askTable_cny, askTable_idr, askTable_php])
setTimeout(() => { askTable_krw.getRows().forEach(function (item, index) {
row = item.getCells();
row.forEach(function (value_tmp){value_tmp.getElement().style.backgroundColor = '';}
)}); }, 200);
setTimeout(() => { askTable_inr.getRows().forEach(function (item, index) {
row = item.getCells();
row.forEach(function (value_tmp){value_tmp.getElement().style.backgroundColor = '';}
)}); }, 200);
color_table Function
function color_table(new_arr, old_array, table_obj){
// If length is not equal
if(new_arr.length!=old_array[0].length)
console.log("Diff length");
else
{
// Comparing each element of array
for(var i=0;i<new_arr.length;i++)
//iterate old dict dict
for (const [key, value] of Object.entries(old_array[0][i])) {
if(value == new_arr[i][key])
{}
else{
// console.log("Different element");
if(key!="TENOR")
// console.log(table_obj)
table_obj.getRows()[i].getCell(key).getElement().style.backgroundColor = 'yellow';
if(key!="TIME")
if(value < new_arr[i][key])
//green going up
//text_to_speech(new_arr[i]['CCY'] + ' ' +new_arr[i]['TENOR']+ ' getting bid')
table_obj.getRows()[i].getCell(key).getElement().style.backgroundColor = 'Chartreuse';
if(key!="TIME")
if(value > new_arr[i][key])
//red going down
table_obj.getRows()[i].getCell(key).getElement().style.backgroundColor = 'Crimson';
}
}
}
}
Potential fudge / solution, thanks Aaron :):
function limiter(fn, wait){
let isCalled = false,
calls = [];
let caller = function(){
if (calls.length && !isCalled){
isCalled = true;
if (calls.length >2){
calls.splice(0,calls.length-1)
//remove zero' upto n-1 function calls from array/ queue
}
calls.shift().call();
setTimeout(function(){
isCalled = false;
caller();
}, wait);
}
};
return function(){
calls.push(fn.bind(this, ...arguments));
// let args = Array.prototype.slice.call(arguments);
// calls.push(fn.bind.apply(fn, [this].concat(args)));
caller();
};
}
This is then defined as a constant for a web worker to call:
const filter_json_run_allLimited = limiter(data => { filter_json_run_all(data); }, 300); // 300ms for examples
Web worker calls the limited function when new web socket data arrives:
// Event to listen for incoming data from the worker and update the DOM.
webSocketWorker.port.addEventListener('message', ({ data }) => {
// Limited function
filter_json_run_allLimited(data);
});
Please if anyone knows how websites like tradingview or real time high performance data streaming sites allow for low latency visualisation updates, please may you comment, reply below :)
I'm reticent to take a stab at answering this for real without knowing what's going on in color_table. My hunch, based on the behavior you're describing is that filter_json_run_all is being forced to wait on a congested DOM manipulation/render pipeline as HTML is being updated to achieve the color-coding for your updated table elements.
I see you're already taking some measures to prevent some of these DOM manipulations from blocking this function's execution (via setTimeout). If color_table isn't already employing a similar strategy, that'd be the first thing I'd focus on refactoring to unclog things here.
It might also be worth throwing these DOM updates for processed events into a simple queue, so that if slow browser behavior creates a rendering backlog, the function actually responsible for invoking pending DOM manipulations can elect to skip outdated render operations to keep the UI acceptably snappy.
Edit: a basic queueing system might involve the following components:
The queue, itself (this can be a simple array, it just needs to be accessible to both of the components below).
A queue appender, which runs during filter_json_run_all, simply adding objects to the end of the queue representing each DOM manipulation job you plan to complete using color_table or one of your setTimeout` callbacks. These objects should contain the operation to performed (i.e: the function definition, uninvoked), and the parameters for that operation (i.e: the arguments you're passing into each function).
A queue runner, which runs on its own interval, and invokes pending DOM manipulation tasks from the front of the queue, removing them as it goes. Since this operation has access to all of the objects in the queue, it can also take steps to optimize/combine similar operations to minimize the amount of repainting it's asking the browser to do before subsequent code can be executed. For example, if you've got several color_table operations that coloring the same cell multiple times, you can simply perform this operation once with the data from the last color_table item in the queue involving that cell. Additionally, you can further optimize your interaction with the DOM by invoking the aggregated DOM manipulation operations, themselves, inside a requestAnimationFrame callback, which will ensure that scheduled reflows/repaints happen only when the browser is ready, and is preferable from a performance perspective to DOM manipulation queueing via setTimeout/setInterval.

How do I Collect User IDs + Retrieve Corresponding Tokens + Send a Push Notification Via Firebase Cloud Function (JS)

The Problem:
I have been unable to use Firebase (Google) Cloud Functions to collect and utilize device tokens for the cloud messaging feature.
Context:
I am a self-taught android-Java developer and have no JavaScript experience. Despite that, I believe I have code that should work and am not sure what the problem is. To my understanding, it could be one of three things:
Somehow my Firebase Realtime Database references are being called incorrectly and I am not retrieving data as expected.
I may need to use Promises to wait for all calls to be made before proceeding, however I don't really understand how I would incorporate that into the code I have.
I may be using multiple return statements incorrectly (which I am also fuzzy on).
My error message on the Firebase Realtime Database console is as follows:
#firebase/database: FIREBASE WARNING: Exception was thrown by user callback. Error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
at FirebaseMessagingError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/srv/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.validateRegistrationTokensType (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:729:19)
at Messaging.sendToDevice (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:328:14)
at admin.database.ref.once.snapshot (/srv/index.js:84:12)
at onceCallback (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:4933:51)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:4549:22
at exceptionGuard (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:698:9)
at EventList.raise (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:9684:17)
The above indicates I am not retrieving data either at all or by the time the return is called. My JavaScript function code is:
'use strict';
const admin = require('firebase-admin');
admin.initializeApp();
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushID}').onCreate((snapshot, context) => {
const valueObject = snapshot.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
sendNotificationPayload(valueObject.uid, payload);
}
index++;
});
});
});
function sendNotificationPayload(uid, payload){
admin.database()
.ref(`/User Token Data/${uid}`)
.once('value', snapshot=> {
var tokens = [];
//if(!snapshot.exists())return;
snapshot.forEach(item =>{
tokens.push(item.val())
});
admin.messaging()
.sendToDevice(tokens, payload)
.then(res => {
return console.log('Notification sent')
})
.catch(err => {
return console.log('Error in sending notification = '+err)
});
});
}
This code is mostly inspired by what was said to be a working example here from another Stack Overflow question here. I have successfully tested sending a notification to a single device by manually copying a device token into my function, so the function does run to completion. My Java code seems to be irrelevant to the problem, so I have not added it (please ask in the comments if you would like it added for further context).
What I Have Tried:
I have tried implementing promises into my code, but I don't think I was doing it properly. My main reference for this was here. I have also looked at the documentation for literally everything related to this topic, however my knowledge of JS is not sufficient to really apply barebones examples to my code.
My Firebase Realtime Database Nodes:
#1: Loop through chat members to collect user IDs:
"Chat Basics" : {
"1607801501690_TQY41wIfArhHDxEisyupZxwyHya2" : {
"Chat Users" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : true,
"TQY41wIfArhHDxEisyupZxwyHya2" : true
},
#2: Collect user tokens from collected IDs (ignore that tokens are matching):
"User Token Data" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi",
"TQY41wIfArhHDxEisyupZxwyHya2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi"
}
Conclusion:
Concrete examples would be much appreciated, especially since I am crunching right now. Thanks for your time and help!
Update:
After some more testing, it looks like the problem is definitely due to my lack of understanding of promises in two areas. Firstly, only one user is collected before the final return is called. Secondly, the final return is called before the 2nd forEach() loop can store snapshot data to an array.
For this code then, how may I modify (or rebuild) it so that it collects all keys before proceeding to retrieve token data from all keys - ultimately before returning the notification?
Just as with every question I post, I managed to figure out how to do it (tentatively) a few hours later. Below is a full example of how to send a notification to chat users based on a message sent (although it does not yet exclude the sender) to a given chat. The order of operations are as such:
User message is saved and triggers event. Relevant data the message contains are:
username, chat key, message
These are retrieved, with (username + message) as the (title + body) of the
notification respectively, and the chat key is used for user id reference.
Loop through chat user keys + collect.
Loop through array of chat user keys to collect array of device tokens.
Send notification when complete.
The code:
//Use firebase functions:log to see log
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushId}').onWrite((change, context) => {
const valueObject = change.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
let promises = [];
var tokens = [];
for(let i=0; i < msgIDs.length; i++){
let userId = msgIDs[i];
let promise = admin.database().ref(`/User Token Data/${userId}`).once('value', snapshot=> {
tokens.push(snapshot.val());
})
promises.push(promise);
}
return Promise.all(promises).then(() => {
return admin.messaging().sendToDevice(tokens, payload);
});
}
index++;
return false;
});
});
});

Initializing web workers

It seems the only communication from host to worker is postMessgage and onmessage. If the worker requires some dynamic initialization (as in constructor, here: regular expression to use later), what is the best way to do this?
A possibility would be to let data be an object, and have e.g. an action parameter, and to check this on every run. This seems a bit kludgey.
The communication channel between different agents is very low-level, but you can easily build a higher level communication based on that. I'd use objects to communicate different "events":
{ event: "init", data: [/d/] }
Based on these events, you can create different events to represent e.g. function calls and their response:
{ event: "call-init", data: [/d/] } // >>>
{ event: "return-init", data: ["done"] } // <<<
Then you can build a wrapper around that, that sends and handles the response, something like:
async function call(name, ...args) {
channel.send("call-" + name, args);
return await cannel.once("return-" + name);
}
channel.init = call.bind(null, "init");
Then your code turns into something along the lines of:
const channel = new Channel(worker);
await channel.init(/d/);
await channel.doOtherStuff();
That's just to give you the basic idea.
A more ad-hoc approach than Jonas' solution abuses the Worker's name option: You can e.g. pass the regex string in this name and use it later:
test.js
var a = new Worker("worker.js", {name: "hello|world"})
a.onmessage = (x) => {
console.log("worker sent: ")
console.log(x)
}
a.postMessage("hello")
worker.js
var regex = RegExp(this.name);
onmessage = (a) => {
if (regex.test(a.data)) {
postMessage("matches");
} else {
postMessage("no match for " + regex);
}
}

Standard way to maintain socket listeners

I have an node.js socket.io application where I have a few different events and listeners. Right now this is how I am doing it.
class testEmitterClass extends events {
}
const testEmitter = new testEmitterClass();
io.on('connection', function (socket) {
console.log('connected');
let dnsInactiveTermsListener = function (dnsInactiveTerms) {
socket.emit(socketEvents.DNS_INACTIVE_TERMS, dnsInactiveTerms);
};
let checkpointInactiveTermsListener = function(checkpointInactiveTerms) {
socket.emit(socketEvents.CHECKPOINT_INACTIVE_TERMS, checkpointInactiveTerms);
};
let dnsActiveTermsListener = function (dnsActiveTerms) {
socket.emit(socketEvents.DNS_ACTIVE_TERMS, dnsActiveTerms);
};
let checkpointActiveTermsListener = function(checkpointActiveTerms) {
socket.emit(socketEvents.CHECKPOINT_ACTIVE_TERMS, checkpointActiveTerms);
};
let dnsCountListener = function (dnsCountStreaming) {
socket.emit(socketEvents.DNS_COUNT, dnsCountStreaming);
};
testEmitter.on(socketEvents.CHECKPOINT_ACTIVE_TERMS, checkpointActiveTermsListener);
testEmitter.on(socketEvents.DNS_INACTIVE_TERMS, dnsInactiveTermsListener);
testEmitter.on(socketEvents.CHECKPOINT_INACTIVE_TERMS, checkpointInactiveTermsListener);
testEmitter.on(socketEvents.DNS_ACTIVE_TERMS, dnsActiveTermsListener);
testEmitter.on(socketEvents.DNS_COUNT, dnsCountListener);
socket.on('disconnect', function () {
console.log('disconnected');
testEmitter.removeListener(socketEvents.DNS_INACTIVE_TERMS, dnsInactiveTermsListener);
testEmitter.removeListener(socketEvents.DNS_ACTIVE_TERMS, dnsActiveTermsListener);
testEmitter.removeListener(socketEvents.DNS_COUNT, dnsCountListener);
testEmitter.removeListener(socketEvents.CHECKPOINT_INACTIVE_TERMS, checkpointInactiveTermsListener);
testEmitter.removeListener(socketEvents.CHECKPOINT_ACTIVE_TERMS, checkpointActiveTermsListener);
})
});
The testemitter is a single instance which is emitting events somewhere else and being sent to the client using socket.io
Is there a way to maintain single list of the listeners somewhere so that this code can be maintained better? How can I map events to the listeners so that they can be added and removed as a client disconnected from socket.io without making a mess.
socketEvents is just an object of event names.
const DNS_COUNT = 'dnsCount';
const DNS_INACTIVE_TERMS = 'dnsInactiveTerms';
const DNS_ACTIVE_TERMS = 'dnsActiveTerms';
const CHECKPOINT_INACTIVE_TERMS = 'checkpointInactiveTerms';
const CHECKPOINT_ACTIVE_TERMS = 'checkpointActiveTerms';
module.exports = {
DNS_COUNT,
DNS_INACTIVE_TERMS,
CHECKPOINT_INACTIVE_TERMS,
DNS_ACTIVE_TERMS,
CHECKPOINT_ACTIVE_TERMS
};
Hope I made myself clear, thanks!
I think you can change the whole way you do things. Rather than register an event handler for every single socket that connects, you can just broadcast the message to all connected sockets. So, I think you can replace everything you show with just this:
class testEmitterClass extends events {
}
const testEmitter = new testEmitterClass();
const notifications = [
CHECKPOINT_ACTIVE_TERMS,
DNS_INACTIVE_TERMS,
CHECKPOINT_INACTIVE_TERMS,
CHECKPOINT_INACTIVE_TERMS,
DNS_COUNT
];
for (let msg of notifications) {
testEmitter.on(socketEvents[msg], function(data) {
// send this message and data to all currently connected sockets
io.emit(socketEvents[msg], data);
});
}
Also notice that the code has been DRYed by using a table of messages that you can loop through rather than repeating the same statements over and over again. So, now to add, remove or edit one of your notification messages, you just modify the table in one place.
If socketEvents (which you don't show) is just an object with these 5 properties on it, then you could even remove the notifications array by just iterating the properties of socketEvents.
That would further reduce the code to this:
class testEmitterClass extends events {
}
const testEmitter = new testEmitterClass();
for (let msg of Object.keys(socketEvents)) {
testEmitter.on(socketEvents[msg], function(data) {
// send this message and data to all currently connected sockets
io.emit(socketEvents[msg], data);
});
}

How to wait for database to finish querying before returning a response?

I am making a React and Electron application that works closely with a local database. I have 2 tables, "blocked" and "unblocked" and the goal of this application is to move a row of data from one table to the other with one click of a button. Most things seems to be working fine but I am having trouble updating my table asynchronously with redux data. Here is the function I have to get the data:
var fetchDBData = (tablename) => {
var db = new sqlite3.Database('processlist.db');
var queries = [];
db.each("SELECT * FROM " + tablename, function(err, row) {
queries.push(row);
});
db.close();
return queries;
};
And I have 2 actions that update redux:
export function updateBlockedFiles(blacklist_queries) {
console.log(blacklist_queries);
return {
type: UPDATE_BLOCKED_FILES,
payload: blacklist_queries
}
};
export function updateWhitelistedFiles(whitelist_queries) {
console.log(whitelist_queries);
return {
type: UPDATE_WHITELISTED_FILES,
payload: whitelist_queries
}
};
And my call in my component looks like this:
unblockProcess(id) {
window.transferRowFromTable('blacklistdb', 'whitelistdb', id);
var blacklist_queries = window.fetchDBData('blacklistdb');
var whitelist_queries = window.fetchDBData('whitelistdb');
this.props.updateBlockedFiles(blacklist_queries);
this.props.updateWhitelistedFiles(whitelist_queries);
};
The problem that I discovered happens mainly in fetchDBData(). When I call this.props.updateBlockedFiles(), it returns me an empty queries array instead of a populated one. I figured this may be an async issue, but I'm not sure how to fix it. I've seen the idea of using promises, but I don't know how to integrate that with React. Any help would help!
I am doing something similar...let me paint the concept and see if it makes sense.
Prior to sending your IPC request to the main process to run the SQL, create an IPC listener in your renderer process to act as a callback when the SQL query is complete. This callback could be constructed to handle data output from the SQLite query. Rough example below:
Renderer Process:
var ipcRenderer = require('electron').ipcRenderer
function handleDBResponse(event, data){
// Do something with the data
}
ipcRenderer.on('dbResponse', handleDBResponse)
ipcRenderer.send('dbQuery', "some_table_name")
Main Process:
var ipcMain = require('electron').ipcMain
function doDBQuery(event, tablename){
// Perform your DB query via SQLite
var db = new sqlite3.Database('processlist.db');
var queries = [];
db.each("SELECT * FROM " + tablename, function(err, row) {
queries.push(row);
});
db.close();
// Send queries back to callback handler
event.sender.send('dbResponse', queries)
}
ipcMain.on('dbQuery', doDBQuery)
This is all done via the IPC methods of Electron to communicate between the main and renderer process. More details can be researched here: https://github.com/electron/electron/blob/master/docs/api/ipc-main.md

Categories