I'm currently developing a WebSocket. For that purpose I've created a JavaScript class. In this class I've 2 methods: subscribe() and bind(). The first method subscribes to a channel and the next one should listen to it.
But when I call my methods this way:
let socket = new CustomWebSocket();
socket.subscribe("my-channel").bind("my-event", function (response) {
console.log(response);
});
I'm getting an error message in my console:
Uncaught TypeError: socket.subscribe(...).bind is not a function
This is how I wrote my functions inside my CustomWebSocket class:
subscribe(channelName) {
let self = this;
let subMsg = {
type: "subscribe",
channel: channelName
};
self.ws.send(JSON.stringify(subMsg));
return channelName;
}
bind(eventName, callback) {
let self = this;
self.ws.onmessage = function (event) {
let eventData = JSON.parse(event.data);
if (eventData.type === "channelMessage") {
callback(eventData.payload);
}
}
}
What did I wrong? It thought it can work this way...
You're returning channelName; from subscribe. So you're effectively trying to call bind on channelName.
To be able to call bind on the return value of subscribe, you need to return self or this from subscribe.
Related
I have the following code:
someClass1 = function () {
this.doStuff = function () {
PubSub.publish('topic1', { id: 1 });
}
}
someClass2 = function () {
this.forename = 'bob2';
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, this.forename);
});
}
function log() {
console.log(arguments);
}
var c1 = new someClass1();
var c2 = new someClass2();
c1.doStuff();
and I am using the pubsubjs library (https://github.com/federico-lox/pubsub.js)
The code is simple - publish a message and handle it in another class (someClass2) using PubSub
My question is that when I publish a message and handle it in someClass2, this is undefined. This occurs at the line: log(msg, data, this.forename);
This means I cant access any of the someClass2 properties/functions. What do I need to do to get the this to not be undefined? Is this possible? Are they other libraries that will help? Am I doing it wrong....
All help apprenticed! Thanks
You're passing an unbound function to subscribe. Such a function has "no idea" about this. You have to bind it:
PubSub.subscribe("topic1", (function (msg, data) {
log(msg, data, this.forename);
}).bind(this));
this is not what you expect inside the callback, just cache a copy of this as another variable outside:
someClass2 = function () {
this.forename = 'bob2';
var that = this;
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, that.forename);
});
}
Im currently using KnockOut JS and i conducted some research about when the observable is notified it will fire a function which is like this
function FunctionToSubscribe()
{
}
var TestObservable = ko.observableArray([]);
TestObservable.subscribe(FunctionToSubscribe);
i am subscribing FunctionToSubscribe in this event
im currently thinking is there a way to unsubscribe it? like we do in c#? when unsubscribing events anyone have an idea regarding this???
The subscribe function returns the "subscription" object which has a dispose method what you can use to unsubscribe:
var TestObservable = ko.observableArray([]);
var subscription = TestObservable.subscribe(FunctionToSubscribe);
//call dispose when you want to unsubscribe
subscription.dispose();
See also in the documentation: Explicitly subscribing to observables
You can use dispose method.
function FunctionToSubscribe()
{
}
var TestObservable = ko.observableArray([]);
// subscribe
var subscriber = TestObservable.subscribe(FunctionToSubscribe);
// unsubscribe
subscriber.dispose();
In my case I needed to temporarily pause the subscription and do some work so I ended up doing this:
ko.subscribable.fn.suspendableSubscribe = function (callback, callbackTarget, event) {
var isSuspended = false;
return ko.utils.extend(this.subscribe(function () {
if (!isSuspended)
return callback.apply(this, arguments);
}, callbackTarget, event), {
suspend: function () { isSuspended = true; },
resume: function () { isSuspended = false; }
});
};
Usage
var name = ko.observable('CodingYoshi');
var subscription = name.suspendableSubscribe(function(){ // code... });
subscription.suspend();
name('CodingYoshi2');
subscription.resume();
I want to prepare some Json Store before processing to callParent(), then it throws an error.
However, me.callParent() works fine outside without async callback.
Ext.define('My.desktop.AppExt', {
extend: 'Ext.ux.desktop.App',
someStore: null,
init: function() {
var me = this;
me.someStore = Ext.create('My.store.SomeStore');
me.someStore.load({
scope: this,
url: 'some/json/url',
callback: function(records, opt, success) {
if (success) {
me.callParent(); // BOOM! ERROR HERE
}
}
});
}
});
ERROR:
Unhandled exception at line 4245, column 17 in //localhost/js/ext-all-debug.js
0x800a138f - JavaScript runtime error:
Unable to get property 'superclass' of undefined or null reference
callParent relies on the context to call the right method, so if you're not actually calling it "directly" from a subclass method, you'll need to invoke it manually:
Ext.define('A', {
foo: function(){
console.log('foo', 'a');
}
});
Ext.define('B', {
extend: 'A',
bar: function(){
this.self.superclass.foo.call(this);
}
});
Ext.onReady(function(){
var o = new B();
o.bar();
});
The best solution for this purpose is to get link to parentMethod like at callParent() function, but without invoking it:
/**
* Returns reference to the parent class method. Works as {#link Ext.Base#callParent}, but doesn't invoke the
* method.
* #return {Function} Parent class method.
*/
getParentMethod: function() {
var method,
superMethod = (method = this.getParentMethod.caller) && (method.$previous ||
((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]));
return superMethod;
},
sampleMethod: function() {
var parentMethod = this.getParentMethod();
var parentArguments = arguments;
someAsyncFunc(function() {
parentMethod.apply(this, parentArguments); // calls parent.sampleMethod(...)
}, this);
}
One of the ways I know is to use extra parameter, that indicates that parent method should be called:
init: function(callParent) {
if (callParent) {
this.callParent();
}
var me = this;
me.someStore = Ext.create('My.store.SomeStore');
me.someStore.load({
scope: this,
url: 'some/json/url',
callback: function(records, opt, success) {
if (success) {
this.init(true);
}
}
});
}
If you use this.self.superclass.init.call(this) it will be ok only until somebody will create child for your class. this.self.superclass points to superclass of instance's class, so it would points to My.desktop.AppExt instead of Ext.ux.desktop.App.
Updated 24.08.2016: Published smarter solution (see another answer by me).
I am struggling with what seems to be a simple concept which makes me think what I am doing can't be done.
In nodejs, if class objectA.emits('hey there'), can class objectB.on('hey there') repsond with 'yo'?
Object A and B have nothing to do with eachother other than they both extend EventEmitter and are in the same nodejs app.
Sorry if this question has been asked before, I can't find it.
Yes
That's pretty much it.
When dealing with Observer/Publisher-Subscriber patterns (or Mediator Patterns), the point is that it really doesn't matter what type of class it is that's doing the "emitting".
Assuming that A is an emitter:
var B = { doStuff : function () { console.log("Yo!"); } };
A.addListener("someEvent", B.doStuff);
A.emit("someEvent");
If you actually want them to talk back and forth, then you need to manually subscribe them to one another...
Assuming that both A AND B are emitters:
B.doStuff = function () { this.emit("B's event", "Yo!"); };
A.doThing = function (param) { console.log(param); };
B.addListener("B's event", A.doThing);
A.addListener("A's event", B.doStuff.bind(B));
A.emit("A's event");
Alternatively, you should look into a Mediator pattern (which also "emits", but is intended to be 1 object which mediates between many objects who don't know one another, but use the same event-names and pass well-defined data-structures, like a good API should).
Assuming that Mediator is an emitter, and A, B and C aren't:
var A = {
getData : function (request) { /* network call */ this.dataCallback(data); },
dataCallback : function (data) { Mediator.emit("data-recieved", data); }
},
B = {
display : document.getElementById("data-display"),
showData : function (data) { /* make DOM representation */ }
},
C = {
input : document.getElementById("request-input"),
button : document.getElementById("request-button"),
getRequest : function () {
var request = this.input.value;
this.requestData(request);
this.disableButton();
},
requestData : function (request) { Mediator.emit("data-request", request); },
disableButton : function () { this.button.disabled = true; },
enableButton : function () { this.button.disabled = false; }
};
Mediator.addListener("data-request", A.getData.bind(A));
Mediator.addListener("data-received", B.showData.bind(B));
Mediator.addListener("data-received", C.enableButton.bind(C));
C.button.addEventListener("click", C.getRequest.bind(C), false);
So now you've got 3 classes who know nothing about one another, each has its own special purpose, and the only expectations that they have of "one another" are that event-names and data-types are appropriate.
They all know about Mediator.
If you want Mediator to be further abstracted, then you can pass a reference to it when you're making your class:
function A (param1, param2) {
var emitter = null;
this.setEmitter = function (myEmitter) { emitter = myEmitter; };
this.emit = function (evt, data) {
if (!emitter) { return; }
emitter.emit(evt, data);
};
this.subscribe = function (evt, callback) {
if (!emitter) { return; }
emitter.addListener(evt, callback);
};
/* rest of the object */
};
var a = new A();
var b = new A();
a.setEmitter(Mediator);
a.subscribe("some-evt", a.doSomething.bind(a));
b.setEmitter(Mediator);
b.subscribe("other-evt", b.doSomethingElse.bind(b));
a.emit("other-evt", { /* data */ });
a and b don't have to be the same class, here, at all.
And now they DO work in the way that you're imagining.
Both have used Dependency Injection ("Inversion of Control") to point to the same emitter (Moderator), so they're both working off of the same event-list, without even knowing it, and using their own methods to subscribe to Moderators events.
im getting frustrated because of this piece of code:
function Model(){
this.GetAjaxData = function(){
//private Member to be returned
var res;
function setRes(newVal){
res = newVal;
alert(newVal); // to verify its really being called
}
// calls a Ajax-Service(1st param) with the given arguments(2nd param),
// the 3rd param is a function with gets called back, commiting the
// output of the Ajax-Service as arguments for the called function
tw.coach.callService(
"GetServerTime",
"<inputs><variable name='input1' type='String'>lala</variable></inputs>",
function(arg){ setRes(arg['Result']); }
);
return res;
};
}
Now, what happens is, once an instance of model has been initialized and the method is being called like:
var _model = new Model();
document.getElementById("myDiv").innerHTML = _model.GetAjaxData();
the alert pops up with the expected data (the Ajax Service simply returns {Result: this came via Ajax.}) but myDiv constains undefined. This tells me that the setRes() is called correctly but it just doesn't set the value of res.
And I have no idea why.
Change your approach taking into consideration async nature of AJAX requests:
function Model() {
this.GetAjaxData = function(callback) {
var data = "<inputs><variable name='input1' type='String'>lala</variable></inputs>";
tw.coach.callService("GetServerTime", data, function(arg) {
callback(arg['Result']);
});
};
}
var _model = new Model();
_model.GetAjaxData(function(res) {
document.getElementById("myDiv").innerHTML = res;
});