How to unbind from all channels / events on pusher - pusher

In the javascript pusher client, is it possible to unbind all callbacks from a particular channel or event ?

Channel inherit from EventDispatcher and it doesn't presently offer an unbind all. You could hack it by re-initialising the callbacks member variable that manages the events on channel:
var pusher = new Pusher( '22364f2f790269bec0a0' );
var channel = pusher.subscribe( 'test-channel' );
var callback = function() {};
for( var i = 0, l = 10; i < l; ++i ) {
channel.bind( 'event-' + i, callback );
}
console.log( channel.callbacks._callbacks );
channel.callbacks = new channel.callbacks.constructor();
You can see this in action here. But this would be a hack.
The best thing to do would be to submit a pull request to add an unbind_all function to the EventDispatcher object.

Related

room broadcast vs sockets emit - are they the same?

I'm using my own rooms implementation in Socket.io (got my own Room/Player classes), since I need to perform several filters the room. For now, I save all sockets inside "players" property in a Room, and implemented my own "emit" to the room, the loops players and emits to their sockets.
Is it considerably slower to the traditional broadcast.to('room')? or it basically does what I did to my own room implementation? I'm aiming on having thousands of rooms with 2-4 players in each...
Thanks :)
As you can see by looking at the code for the socket.io adapter .broadcast() on GitHub, all socket.io is doing is looping over a list of sockets and sending a packet to each one (see code below).
So, if your code is doing something similar, then performance would likely be something similar.
Where you might notice a feature difference is if you are using a custom adapter such as the redis adapter that is used with clustering, then the logic of broadcasting to users connected to different servers would be handled for you by the built-in adapter, but may be something you'd have to implement yourself if doing your own broadcast.
Here's the socket.io-adapter version of .broadcast():
Adapter.prototype.broadcast = function(packet, opts){
var rooms = opts.rooms || [];
var except = opts.except || [];
var flags = opts.flags || {};
var packetOpts = {
preEncoded: true,
volatile: flags.volatile,
compress: flags.compress
};
var ids = {};
var self = this;
var socket;
packet.nsp = this.nsp.name;
this.encoder.encode(packet, function(encodedPackets) {
if (rooms.length) {
for (var i = 0; i < rooms.length; i++) {
var room = self.rooms[rooms[i]];
if (!room) continue;
var sockets = room.sockets;
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
if (ids[id] || ~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) {
socket.packet(encodedPackets, packetOpts);
ids[id] = true;
}
}
}
}
} else {
for (var id in self.sids) {
if (self.sids.hasOwnProperty(id)) {
if (~except.indexOf(id)) continue;
socket = self.nsp.connected[id];
if (socket) socket.packet(encodedPackets, packetOpts);
}
}
}
});
};

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");}

How do I output a stream of tuples from a Storm spout with emit() and sync()?

(xpost github issue)
I'm new to Storm. I found the helpful node-storm library and I have successfully submitted topologies, but I can't get my spout to emit a stream of tuples.
node-storm's wordcount example works fine.
I want a spout that subscribes to a websocket and outputs any messages as tuples.
Here's my attempt so far. I think I have some misconfiguration, because I know I my wsEmitter is emitting future events, but my Storm UI shows zero spout emits.
I suspect that maybe I shouldn't be binding the listener inside the spout function?
Does this function get invoked multiple times? (looks like it... see https://github.com/RallySoftware/node-storm/blob/master/lib/spout.js#L4 )
What does sync actually do and when should I use it?
var storm = require('node-storm');
var wsEmitter = require('./wsEmitter.js')();
wsEmitter.init(); // subscribe to websocket
var futuresSpout = storm.spout(function(sync) {
var self = this;
console.log('subscribing to ws');
wsEmitter.on('future', function(data){ // websocket data arrived
self.emit([data]);
sync();
});
})
.declareOutputFields(["a"]);
Turns out I had two problems. First, my topology wasn't executing because one of my bolts (not shown) failed to set .declareOutputFields().
Second, I need to delay the emits from the spout until the supervisor asks for one emit with nextTick(). I did that by buffering any incoming messages until the supervisor calls the spout:
module.exports = (function(){
var storm = require('node-storm');
var wsEmitter = require('./wsEmitter.js')();
wsEmitter.init();
var queue = [];
var queueEmpty = true;
wsEmitter.on('thing', function(data){
var trade = JSON.parse(data);
trade.timeReported = new Date().valueOf();
queue.push(trade);
queueEmpty = false;
});
return storm.spout(function(sync) {
var self = this;
setTimeout(function(){
if(!queueEmpty){
self.emit([queue.shift()]);
queueEmpty =
( queue.length === 0
? true
: false )
}
sync();
}, 100);
})
.declareOutputFields(['trade'])
})()

Pusher: How to bind to 100s of events?

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 ).

Resources