Manually firing off signal event? - javascript

I'm wondering if there is a way to manually cause a simplepeer Peer object to fire of the signal data event that does of when a initiating peer is created.
More precisely what I want to do is to create Peer objects with the initiator property set to false and then manually turn them into initiating peers.
Is this possible or should I look elsewhere?
Update 1:
I tried to create a non initiating peers and then set initiator to true via function but that did not trigger the signal event.
so basically this:
let p = new SimplePeer();
p.on('signal', function (data) {
console.log('SIGNAL', JSON.stringify(data))
});
const turnOn = function () {
p.initiator = true;
};
turnOn();
*This is not the actual code, just the parts that have to do with the question.

Related

Socket io 'on' callback running multiple times

I have a react app that uses socket io to send for and receive all data. I created a global socket variable to be shared across all components...
export let gameScoket = null
export const update = (newSocket) => gameScoket = newSocket
I then set the socket in 'Home.jsx' and make a few calls...
update(io("ws://localhost:8888"))
socket = gameScoket
socket.on('...')
The problem arose when adding a callbacks to these calls. The callbacks seems to be called a random (very large) amount of times, increasing every time the socket is used. An example of this can be seen in these three sockets in 'Game.jsx'...
socket.on("question-update", (data) => {
console.log("Calling question-update")
const responce = JSON.parse(data)
setQuizData(responce.data)
})
socket.on("point-update", (data) => {
console.log("Calling point-update")
const responce = JSON.parse(data)
setUsrData(responce.data)
})
socket.on("personal-point-update", (data) => {
console.log("Calling personal-point-update")
const responce = JSON.parse(data)
setClientScore(responce.data)
})
Whilst there is no evidence of the client spamming the server with requests, the console is bombed with messages, and the state is re-updated so many times the app becomes unresponsive and crashes. Here's a screenshot of the console...
I don't know where I went wrong with my implementation and would love some advice, thanks.
try to use socket.once("",(data)=>{});
The bug in your code is that each time a new connection() is called node registers an event listener 'Quest'. So first time new connection() is called the number of event listeners with event 'Quest' is one, the second time function is called, the number of event listeners increases to two, and so on
socket. once() ensures that the number of event listeners bound to a socket with event 'Quest' registered is exactly one
Make sure to keep all 'socket.on()' call within useEffect to prevent duplication.
useEffect(() => {
socket.on('...')
}, [])

How to avoid race conditions when assigning custom methods to WebSocket?

When I look at tutorials/documentation about WebSockets I find code like this:
var ws = new WebSocket("ws://localhost:8765/dlt");
ws.onopen = () => {
// do some very important stuff after connection has been established
console.log("onopen");
}
But what about race conditions here? Are there somehow avoided in JavaScript?
For example this code (which just assigns onopen after the connection has been opened) will fail:
var ws = new WebSocket("ws://localhost:8765/dlt");
setTimeout(() => {
ws.onopen = () => {
// do some very important stuff after connection has been established
console.log("onopen"); /// <== won't be called
}
}, 100);
Can I be sure that the assignment has been done before the connection get's established?
(I tried to extend WebSocket with a custom onopen() method but this doesn't seem to work)
class MyWebSocket extends WebSocket {
onopen() {
console.log("onopen()");
/// do some very important stuff after connection has been established
}
}
You should have a read about javascript's event loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Event_loop
If you look at the section about Run-to-completion, you get this useful explanation:
Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.
So in your example, the assignment to ws.onopen must be completed before the websocket does anything asynchronous in nature. By putting your assignment inside setTimeout, you are moving it outside of the currently running context, and so it may not be executed before it is required by the websocket.
You should rest assured that the example is ok. The Javascript event loop will finish the current task before assuming any other tasks. This means that 1) the WebSocket cannot open the connection (async operation) before the onopen event, 2) the onopen event handler will be called during the following cycles.
Setting the timeout on the other hand will complicate matters, because the events will be called in some order after the current task. This means that that the WebSocket has chance to open the connection before the handler has been set.

JavaScript same functions, different implementation decided on runtme

What is the best way to change JavaScript implementations at run time?
I have a web application which connects to the server by SignalR.
If there is any problem connecting to the server using SignalR at runtime, I want to change the services functions implementations to work with regular XHR.
I have one js file with the following functions to connect via SignalR:
function initializeConnection() {
// Initialize connection using SignalR
}
function sendEcho() {
// Sending echo message using signalR
}
And another js file with the same functions for connection via XHR:
function initializeConnection() {
// Initialize connection using XHR
}
function sendEcho() {
// Sending echo message using XHR
}
I know it is impossible to have them loaded at the same time.
I know I can use one file with a toggle within each function.
I thought maybe I can switch between these files by loading & unloading them at runtime. Is this possible? If so, is this the best way for such an issue?
What is the best way for supplying different implementations at runtime?
One way to do it, is to define both implementations as objects with same signatures and just set the namespace to a variable:
;var MyStuff = {
//SignalR
SignalR: {
initializeConnection: function(){console.log('SignalR.initializeConnection()')},
sendEcho: function(){console.log('SignalR.sendEcho()')}
},
//XHR
XHR: {
initializeConnection: function(){console.log('XHR.initializeConnection()')},
sendEcho: function(){console.log('XHR.sendEcho()')}
}
};
//Do whatever check you want to
var mNamespace = (1 === 2) ? MyStuff.SignalR : MyStuff.XHR;
//Call the instance
mNamespace.initializeConnection();
You can also keep them split in two files and add them both to MyStuff dynamicallly:
//File 1
;var MyStuff = (MyStuff === undefined) ? {} : MyStuff;
MyStuff.SignalR = {..};
//File 2
;var MyStuff = (MyStuff === undefined) ? {} : MyStuff;
MyStuff.XHR = {..};
One pattern that can help you is the "lazy function definition" or "self-defining function" pattern. It consists of (as its name points out) the redefinition of a function at runtime. It's useful when your function has to do some initial preparatory work and it needs to do it only once.
In your case, this "preparatory" work would be selecting the function that handles the client-server connection.
For instance:
var sendMessage = function() {
// Perform a check, or try a first message using your default connection flavour
// Depending on the result, redefine the function accordingly
sendMessage = sendMessageUsingWhatever;
};
//Use sendMessage anywhere you want, it'll use the proper protocol
This pattern was particularly handy when dealing with browsers and their peculiarities:
var addHandler = document.body.addEventListener ?
function(target, eventType, handler) {
target.addEventListener(eventType, handler, false);
} :
function(target, eventType, handler) {
target.attachEvent("on" + eventType, handler);
};
In this case, it is useful to determine which which way to attach event listeners depending on the availability (or not) of a particular method.
It has its drawbacks though. For instance, any properties you've previously added to the original function will be lost when it redefines itself.
Hope it helps or at least gives you some ideas.

socket.io, adding message handler dynamically

I've written happily a node.js server, which uses socket.io to communicate with the client.
this all works well.
the socket.on('connection'...) handler got a bit big, which made me think of an alternative way to organize my code and add the handlers in a generator function like this:
sessionSockets.on('connection', function (err, socket, session) {
control.generator.apply(socket, [session]);
}
the generator takes an object that contains the socket events and their respective handler function:
var config = {
//handler for event 'a'
a: function(data){
console.log('a');
},
//handler for event 'b'
b: function(data){
console.log('b');
}
};
function generator(session){
//set up socket.io handlers as per config
for(var method in config){
console.log('CONTROL: adding handler for '+method);
//'this' is the socket, generator is called in this way
this.on(method, function(data){
console.log('CONTROL: received '+method);
config[method].apply(this, data);
});
}
};
I was hoping that this would add the socket event handlers to the socket, which it kind of does, but when any event comes in, it always calls the latest one added, in this case always the b-function.
Anyone any clues what i am doing wrong here?
The problem appears because by that time this.on callback triggers (let's say in a few seconds after you bind it), the for loop is finished and method variable becomes the last value.
To fix that you may use some JavaScript magic:
//set up socket.io handlers as per config
var socket = this;
for(var method in config){
console.log('CONTROL: adding handler for '+method);
(function(realMethod) {
socket.on(realMethod, function(data){
console.log('CONTROL: received '+realMethod);
config[realMethod].apply(this, data);
});
})(method); //declare function and call it immediately (passing the current method)
}
This "magic" is hard to understand when you first see it, but when you get it, the things become clear :)

add to WebSocket.onmessage() like how jQuery adds to events?

I'm writing a single page ws++ site, and I'd like to keep my code grouped first by "page" (I think I need a new word since it never posts back) then by section then by concept etc.
I'd like to split up WebSocket.onmessage across my code much in the same way that $('#someElement') can constantly have an event like click(function(){}) added to it.
Can this be done with WebSocket.onmessage(function(){})? If so, how?
As some jQuery programmers happily know, an event can be initially set then added to in multiple places across the js. That's my favorite thing about js, the "put it anywhere as long as it's in order" ability. This makes code organization so much easier for me at least.
With WebSockets, really, the action client side for me so far is with the WebSocket.onmessage() handler since WebSocket.send() can be used anywhere and really just ports js data to the server.
onmessage() now owns my page, as whatever's in it initiates most major actions such as fading out the login screen to the first content screen upon a "login successful" type message.
According to my limited understanding of js, the onmessage() handler must be set all in one place. It's a pain to keep scrolling back/tabbing to another file to make a change to it after I've changed the js around it, far, far, away.
How can I add to the WebSocket.onmessage() handler in multiple places across the js?
To answer your last question;
how can I add to onmessage handler in multiple places across the js?
You can define your own personal (global) event handler in which you accept arbitrary number of handler functions. Here's an example:
window.bind: function(name, func, context) {
if (typeof this.eventHandlers[name] == "undefined") {
this.eventHandlers[name] = [func];
this.eventContexts[name] = [context];
}
else {
var found = false;
for (var index in this.eventHandlers[name]) {
if (this.eventHandlers[name][index] == func && this.eventContexts[name][index] == context) {
found = true;
break;
}
}
if (!found) {
this.eventHandlers[name].push(func);
this.eventContexts[name].push(context);
}
}
}
window.trigger: function(name, args) {
if (typeof this.eventHandlers[name] != "undefined") {
for (var index in this.eventHandlers[name]) {
var obj = this.eventContexts[name][index];
this.eventHandlers[name][index].apply(obj, [args]);
}
}
}
// === Usage ===
//First you will bind an event handler some where in your code (it could be anywhere since `.bind` method is global).
window.bind("on new email", function(params) { ... });
//Then you need to trigger "on new email" in `onmessage` once appropriate events happen.
WebSocket.onmessage(function(data) {
//Work on data and trigger based on that
window.trigger("on new email", { subject: data.subject, email: data.email });
})
This code is a part of an open source project I worked on before. It gives events names and let you set context for your handler (for methods instead of functions). Then you can call trigger in your onmessage handler of your socket. I hope this is what you are looking for.
You can create a wrapper which will handle WS events on itself. See this example CoffeeScript:
class WebSocketConnection
constructor: (#url) ->
#ws = new WebSocket(#url)
#ws.onmessage = #onMessage
#callbacks = []
addCallback: (callback) ->
#callbacks.push callback
onMessage: (event) =>
for callback in #callbacks
callback.call #, event
# and now use it
conn = new WebSocketConnection(url)
conn.addCallback (event) =>
console.log event
You can do it with addEventListener :
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
I've constructed a CoffeeScript class to solve this problem. It's similar to #Valent's but a bit more full-featured, so I figured I'd share it. It provides "on", "off", and "clear" methods for web socket events and also provides forwarding functions for "send" and "close" so that you pretty much don't have to touch the socket directly. If you do need access to the actual WebSocket object, you can get there by superWebSocket.ws.
edit: I added a getConnection static method to produce url-dependent singletons. This way there's only one connection per url and if you attempt to create a 2nd, it just gives you the existing one. It also protects against anyone calling the constructor directly.
edit: I communicate across the socket in JSON. I added some code that will run JSON.stringify on any non-string passed into send and also will attempt to run JSON.parse on any message received via a handler.
superSockets = {}
class SuperWebSocket
#getConnection: (url)->
superSockets[url] ?= new SuperWebSocket url
superSockets[url]
constructor: (url)->
if arguments.callee.caller != SuperWebSocket.getConnection
throw new Error "Calling the SuperWebSocket constructor directly is not allowed. Use SuperWebSocket.getConnection(url)"
#ws = new WebSocket url
events = ['open', 'close', 'message', 'error']
#handlers = {}
events.forEach (event)=>
#handlers[event] = []
#ws["on#{event}"] = (message)=>
if message?
try
message = JSON.parse message.data
catch error
for handler in #handlers[event]
handler message
null
on: (event, handler)=>
#handlers[event] ?= []
#handlers[event].push handler
this
off: (event, handler)=>
handlerIndex = #handlers[event].indexOf handler
if handlerIndex != -1
#handlers[event].splice handlerIndex, 1
this
clear: (event)=>
#handlers[event] = []
this
send: (message)=>
if typeof(message) != 'string'
message = JSON.stringify message
#ws.send message
close: => #ws.close()

Categories

Resources