Pusher: How to bind to 100s of events? - pusher

The push library works as below
var channel = pusher.subscribe('test_channel');
channel.bind('my_event', function(data) {
alert(data.message);
});
However:
Would I be able to do this?
var channel = pusher.subscribe('test_channel');
channel.bind(['my_event1', 'my_event2'....'my_event100'], function(data) {
alert(data.message);
});
In my use case, I have one channel and there are many different events and each client might want to simulantaneously subscribe to 100s of events.

The signature for the channel.bind function is String channelName, Function callback (pusher-js source). You can't pass in an Array of channels`.
If you want the same function to be called then you'll need to pass a reference to the function and call bind multiple times:
var channel = pusher.subscribe('test_channel');
var callback = function(data) {
alert(data.message);
};
var eventName;
for( var i = 0; i < 100; ++i ) {
eventName = 'my_event' + ( i + 1 );
channel.bind( eventName, callback );
}
The single-threaded nature of JS will equate to these event binding happening simultaneously.
You could of course create your own helper function to allow bind( Array eventNames, Function callback ).

Related

Adding listeners in nodeJS if the listener is not already added and listener handler is an anonymous function

Is there a way to check if a listener already exist for an object in node.js?
I want to implement the following scenario:
Get an object of db
Do some operation
Add listeners eg error, result, drain etc if the same listener is not already added [assume that for all the operations the listener operation is same]
I wanted to optimize the addition of listeners in such a way that new listeners wont be added if we try and add an existing listener. Node documentation says "No checks are made to see if the listener has already been added. Multiple calls passing the same combination of eventName and listener will result in the listener being added, and called, multiple times."
Is there a way around it?
[EDIT]-Adding some sample code
connpool.getConnection(function(err, connection) {
var querystr = "Some valid SQL query";
connection.execute(querystr, data, function(err, rows) {
if (err) {
console.error(err);
}
connection.on('error', function(err){onErr(err,connection);});
do some stuff
cleanup(connection);
});
})
var onErr = function(err, connection) {
console.error({"Error message"});
connection.release();
cleanup(connection);
};
var cleanup = function(conn) {
conn.removeListener('error',onErr);
};
Connection will contain a db connection and its coming from an external package.In the statement connection.on('error', function(err){onErr(err,connection);}); i'm using an anonymous function as i need to pass an extra argument to the cleanup method. During cleanup i dont get a handler to the function as i'm using an anonymous function.
As long as you keep a reference to the listener when you hook it, you can check if it is in the array of listeners returned by emitter.listeners(eventName).
Rough example (I'm sure it could be more efficient)
/**
* Created by cool.blue on 8/4/2016.
* http://stackoverflow.com/q/38700859/2670182
*/
const EE = require('events');
const util = require('util');
var host = new EE();
// set up a emitter with n events
const n = 10;
const events = Array.apply(null, Array(n)).map((x, i) => 'event_' + i);
events.forEach(function(e){
host.on(e, function g() {console.log(e)})
});
console.log(util.inspect(host));
// get a reference to one of the listener functions
const target = 'event_3';
var probe = host.listeners(target)[0];
// add a method to only add unique listeners
host.onUnique = function (type, listener){
var slot = this.listeners(type).find(function(l) {
return l === listener
});
if(slot)
return this;
console.log('adding');
return this.on(type, listener)
};
// try to add the same listener again
var count0 = host.listenerCount(target);
var count1 = host.onUnique(target, probe).listenerCount(target);
console.log('added ' + (count1 - count0) + ' listeners'); // added 0 listeners
console.log(util.inspect(host));
// try to add a new listener
count0 = host.listenerCount(target);
count1 = host.onUnique(target, function h(){ console.log('different cb')}).listenerCount(target);
console.log('added ' + (count1 - count0) + ' listeners'); // added 1 listeners
console.log(util.inspect(host));
In response to the updated question...
you could do something like this...
TL;DR
the basic idea is to use a non-anonymous function for the listener and pass a reference to it and the connection to the utility functions in the outer scope.
const EE = require('events');
const util = require('util');
(function manage(host){
host.name = 'host';
host.release = function(){
console.log('released!')
};
function l(err) {
onErr(err, host, l)
}
l.e = 'error';
host.on('error', l);
if(Math.random() > 0.5)
host.emit('error', new Error('oops!'));
if(l.e)
cleanUp(host, l, 'manage');
})(new EE());
function onErr(e, h, l) {
console.error(`\n${h.name}: ${e.message}`);
h.release();
cleanUp(h, l, 'onError')
}
function cleanUp(h, l, context){
console.log('\n\x1b[33m' + context + '\n'
+ 'before:\t' + h._eventsCount + '\x1b[0m\n' + util.inspect(h));
h.removeListener(l.e, l);
console.log('\n\x1b[33mafter:\t' + h._eventsCount + '\x1b[0m\n' + util.inspect(h));
delete l.e
}
The IIFE is just to simulate your situation where there is no reference to host (connection) in the outer scope.

why node js donot provide a [Mother]function to call any function asynchronously with a supplied call back

Given Node.js boasts of asynchronous event driven model,
I was expecting, I should be able to write any Nodejs function,
e.g as simple as going through a loop, e.g IamLooper() below,
which might or might not involve file I/O and then pass that looping function to a mother nodeJs function e.g Invoke(),to which I also pass another call back functiont e.g happyend() below.
My expectation was after IamLooper is finished ,happyend () will be invoked by the NodeJs supplied function .
e.g :
==>
gdata =[];
function IamLooper() {
var pi = Array;
for (var ii = 0 ; ii <4 ; ii ++)
{
pi[ii] = 13* ii;;
gdata.push(ii);
}
console.log("looper done -tell the callback") ;
}
function happyend() { console.log("looper says done");}
I want to invoke IamLooper() and supply the happyend at time of invocation.
i.e. I am looking for a ready made node function e.g Invoke, which can be called like this:
Invoke(IamLooper(), happyend());
if(gdata.length > 0) {console.log("looping has started");}
In essence Invoke should do the same for any two functions I supply to it so that we have just a working template of a callback execution strategy.
Also the Invoke being executed async, my program progresses beyond Invoke before it finishes.
Is my expectation is misguided ? Can any one give me some guidance here.
If you are looking for a preexisting way of easily doing callbacks in node, you should use event emitters (https://nodejs.org/api/events.html):
var EventEmitter = require('events').EventEmitter;
var eventExample = new EventEmitter;
//You can create event listeners:
eventExample.on('anEvent', function(someData){
//Do something with someData
});
//To trigger an event listener you must emit:
eventExample.emit('anEvent', someData);
With your code, it'd look something like this:
var EventEmitter = require('events').EventEmitter;
var looper = new EventEmitter;
looper.on('invoke', function(data){
var callFunction = data.callFunction;
var finishFunction = data.finishFunction;
var callParameters = data.callParameters;
var finishParameters = data.finishParameters;
if(callParameters == null){
callFunction({callbackPara: finishParameters, callbackFunction: finishFunction});
}
else{
callFunction(callParameters, {callbackParameters: finishParameters, callbackFunction: finishFunction});
}
});
looper.on('finish', function(data){
var finishFunction = data.callbackFunction;
var parameters = data.callbackParameters;
if(parameters == null){
finishFunction();
}
else{
finishFunction(parameters);
}
});
gdata =[];
function IamLooper(g, callback){
var pi = Array;
for (var ii = 0 ; ii <4 ; ii ++){
pi[ii] = 13* ii;;
g.push(ii);
}
looper.emit('finish', callback);
}
function happyend() { console.log("looper says done");}
And then call it like:
looper.emit('invoke', {callFunction: IamLooper, finishFunction: happyend, callParameters: gdata, finishParameters: null});
You can also always do normal callbacks:
gdata =[];
function IamLooper(g, callback){
var pi = Array;
for (var ii = 0 ; ii <4 ; ii ++){
pi[ii] = 13* ii;;
g.push(ii);
}
callback();
}
IamLooper(gdata, function(){ console.log("looper says done");}

Node.js + Socket.io Storing data in sockets

I am currently building an application using node.js and using the socket.io module. When a user connects I am storing data specific to the user against their socket. For example
io.sockets.on('connection', function (socket) {
socket.on('sendmessage', function (data, type) {
socket.variable1 = 'some value';
socket.variable2 = 'Another value';
socket.variable3 = 'Yet another value';
});
});
While this works my question is, is this a good way to do it. I am effectively storing session data but is there a better way to do it?
I think that you should store those variables in another type of object. Keep the socket object only for the communication. You may generate an unique id for every user and create a map. Something like this:
var map = {},
numOfUsers = 0;
io.sockets.on('connection', function (socket) {
numOfUsers += 1;
var user = map["user" + numOfUsers] = {};
socket.on('sendmessage', function (data, type) {
user.variable1 = 'some value';
user.variable2 = 'Another value';
user.variable3 = 'Yet another value';
});
});
Update: io.set() and io.get() methods are deprecated
A reasonable way is to choose a data store and associate each data with a unique socket identifier (id, for example).
A recommended way is to use the native socket.set and socket.get to set and get data asynchronously specifically to the current socket.
Following your example:
io.sockets.on('connection', function (socket) {
socket.on('sendmessage', function (data, type) {
socket.set('variable1', 'some value');
socket.set('variable2', 'Another value');
socket.set('variable3', 'Yet another value');
});
});
Also, you can call a function asynchronously after you set a value:
...
socket.set('variable1', 'some value', function () {
/* something to be done after "variable1" is set */
});
...
Finally, you can retrieve a variable doing:
...
var variable1 = socket.get('variable1')
...
Or use it directly when needed:
if ( socket.get('age') > 30 ) {
// Vida longa às eleições presidenciais diretas no Brasil
}

Async foreach in nodejs

In node.js, I know array.forEach is blocking.
I need to loop through an array and build a string like this:
var rarray = ['test', 'test2'];
var rstring = '';
rarray.forEach(function(e, i){
rstring = rstring + i + e;
});
return rstring;
how do i do it asynchronously?
In such a case, you probably don't need to transform this code into its async version.
Hypothetically, or for very very large arrays, heres's a way to transform your code into something asynchronous, or at least something that will get back to the main event loop periodically.
var rarray = ['test', 'test2'];
var rstring = '';
var max = rarray.length;
var current = 0;
process.nextTick(function next() {
rstring = rstring + rarray[current++];
if (current >= max) {
console.log("done", rstring);
} else {
process.nextTick(next);
}
});
In practice you'd wrap this in a function, and replace console.log by calling the completion callback.
I might add that what you are trying to do is actually called reduce.
you can write it this way (doc)
var res = array.reduce(function(prev, current, index) {
return prev + index + current ;
}, '');
doing it asynchronously could be done this way
var array = ['one', 'two'];
function reduceAsync(collection, initial, process, callback) {
var i = 0;
var res = initial;
function DO(err, result) {
if(err) return callback(err);
if(i > collection.length) return callback(null, res);
var index = i++;
var value = collection[index];
process(res, value, index, collection, DO);
}
DO(null, res);
}
reduceAsync(array, '', function(previous, current, index, collection, callback) {
setTimeout(function() {
callback(null, previous + index + current);
}, 10); // wait 10 ms
}, function finalResult(err, result) {
console.log(result);
})
or, you know, you could use async.reduce
Pascal's answer is essentially a form of cooperative multithreading (See Wikipedia: Thread).
It's hard to say without measuring, but I would guess that preemptive multithreading would have better throughput for things of this nature, because the compiler has the opportunity to do loop optimizations (not sure that would happen specifically with the code above), and the operating system is likely better at deciding exactly how often to task-switch. It looks like you can do Web Worker Threads in node.js.
Anybody have benchmarks?
If you want to use the async module with the code above, you would still end up with synchronous code. The async module allows you to avoid callback hell and manage your callbacks. It doesn't make something synchronous async. To achieve that you need to use process.nextTick like in Pascal's answer.
If you were doing some extra async processing on each item in your array and you want to aggregate the result from each operation while preserving order then you could use async.eachSeries like this:
var async = require('async');
var rarray = ['test', 'test2'];
var rstring = '', i = 0;
async.eachSeries(rarray, function(item, callback){
rstring += (i++) + item;
callback();
}, function(err){
console.log(rstring);
}
);
If you are not fussed about the order of things, then you could use async.each and it will execute your async processing function in parallel.

flapjax get last element from eventstream

I am trying to implement a small chatservice using flapjax. I use an eventStream to get all the clients that connect to the server, and when broadcasting a message (the function on 'message') I map over this eventStream with the function that emits the message to the current client.
// Event stream yielding received clients
var clientReceiverE = receiverE();
// Event stream collecting all the clients
var clientsE = clientReceiverE.collectE([], function (client, clients) {return clients.concat([client]);});
socket.on('connection', function(client) {
clientReceiverE.sendEvent(client);
for (i = 0; i < chatMessages.length; i++) {
client.emit('message', chatMessages[i]);
}
client.on('message', function(message) {
chatMessages.push(message);
//for (i = 0; i < clients.length; i++) {
// clients[i].emit('message', message);
//}
mapE(clientReceiverE, function(client) {console.log(client); client.emit('message', message); return client});
});
client.on('nickname', function(name) {
});
});
The registring of the clients on the eventstream succeeds with this code, but the mapE doesn't result in a loop over all this clients. Does anybody know what is wrong here?
If you are still not guessed :) I think it's because mapE doesn't produce any action itself, mapE only creates and returns another EventStream which behaves like a given source, but with modified value by means of a given function.
You should not be using mapE like that. In your code you are attempting to recreate the mapE event bindings with each client.on('message', ...).
This problem is solved using a receiverE. This function is used to translate, external event streams into flapjax EventStream 's.
// Event stream yielding received clients
var clientReceiverE = receiverE();
// Event stream collecting all the clients
var clientsE = clientReceiverE.collectE([], function (client, clients) {return clients.concat([client]);});
var clientsB = clientsE.startsWith(undefined); //Turn the event stream into a behaviour (event + value)
var messagesE = receiverE();
messagesE.mapE(function(messagePacket){
var clients = clientsB.valueNow(); //Grab current value of client list behaviour
if(clients==undefined){
return;
}
var from = messagePacket.client;
for(var index in clients){
clients[index].emit('message', messagePacket.message);
console.log(messagePacket.message);
}
});
socket.on('connection', function(client) {
clientReceiverE.sendEvent(client);
client.on('message', function(message) {
messagesE.sendEvent({client: client, message: message});
});
});
The difference is this. The flapjax tree is isolated from the WebSocket event code and there is no shared state between them.

Resources