I am building a middleware layer between my Socket.IO events and the rest of my application. I do this so that I can swap out Socket.IO for something else in the future.
I store callback functions in an array. When a specific event triggers, I loop over the array and execute the callback functions. That works like a charm.
The problem lies in the removing of a callback from that array. When a callback function needs to be removed, I loop over the array and check every array item to see if it is equal (using ===
) to the callback I want to remove. When just the callback is stored in the array, this works fine. However, when a callback is stored in combination with .bind()
, the equal check returns false.
I created a (simplified) codepen to demonstrate the problem: http://codepen.io/petergoes/pen/wWPJdg?editors=0012.
I draw inspiration from the Socket.IO code to write my removal method: https://github.com/socketio/socket.io-client/blob/master/socket.io.js#L1623. However, there the same problem occurs.
The big question:
How can I compare two functions when (one or both of them) are called with the .bind()
method?
I found this answer how do I compare 2 functions in javascript. However, comparing the string version of a function feels a bit sketchy
The codepen for reference:
var callbackList = [];
var objA = {
myCallbackFunction: function myCallbackFunction() {
console.log('hello my name is:', this.myName);
}
}
var objB = {
register: function register(callback) {
console.log('register callback');
callbackList.push(callback);
},
remove: function remove(callback) {
console.log('remove callback');
if(callbackList[0] === callback) {
console.log('callback found, splice it');
callbackList.splice(0, 1);
} else {
console.log('callback NOT found!');
}
console.log('callbackList length:', callbackList.length);
}
}
objB.register(objA.myCallbackFunction.bind({myName: 'Peter'}));
objB.remove(objA.myCallbackFunction.bind({myName: 'Peter'}));
console.log('\nreset callbackList\n');
callbackList = [];
objB.register(objA.myCallbackFunction);
objB.remove(objA.myCallbackFunction);
I think the best solution here is to store a key alongside your callback inside of the array. This is a lot more reliable and way less hacky than any solution comparing functions:
register: function register(callback, key) {
console.log('register callback');
var obj = {
key: key,
callback: callback
}
callbackList.push(obj);
}
you can then call remove by passing the key desired rather than the callback, and you can compare as such:
if(callbackList[0].key === key)
Just make sure you pass the desired key when registering and access the callback property within the array objects where necessary.
I think this solution using labels is much cleaner and doesn't require any confusing or strange code.
Consider this:
function a(x) { return x; }
var b = a.bind({ foo: 'foo' });
a !== b
is true
because a.bind
returns a new function, that is a new object, and object are compared by reference.
The hack:
Keep in mind messing up with native prototypes is not recomended, this is a nasty hack and using keys as @Pabs123 said might be a better approach
Function.prototype.bind = (function () {
var originalBind = Function.prototype.bind;
return function (obj) {
var bound = originalBind.apply(this, Array.prototype.slice.call(arguments, 1));
bound.original = this;
return bound;
};
}());
Now have a reference to the function that has been bound and you can check that one too when comparing:
a = function (x) { return x; };
b = a.bind({});
b.original === a; // true
Note that the hack must be implemented before any binding is done, any calls to bind
before the hack executes will not produce the original
property on the result function.
A simmilar approach, but without doing the hack would be to do your custom bind method that keeps the reference to the original function. But you'll need to use this method instead of the native bind
in order to get the reference to the original function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With