Given the code above:
binaryServer = BinaryServer({port: 9001});
binaryServer.on('connection', function(client) {
console.log("new connection");
client.on('stream', function(stream, meta) {
console.log('new stream');
stream.on('data', function('data'){
//actions
stream.on('end', function() {
//actions
});
});
});
I can say that client inherits the features of binaryServer. So if I make console.log(client.id) in the events of stream I can see, which client generate the given event. Now I want to know if every single event is exclusive of one client, in other words I want to know if data happens for every single client (that generates data) and no data event will be generated while the actions is happening.
You're registering a listener to the "connection" event which can happen within binaryServer. When a "connection" event happens, the registered listener will receive an argument, which you choose to call client. client in this case is an object, and doesn't inherit features of binaryServer.
"data" happens for every client, but will have unique results for each clientsince you register an event listener for every client.
If two events are triggered after each other, the callback function of the first event will be called, and after that the second events callback function will be called. See the following example code:
var event = new Event('build');
var i = 0;
// Listen for the event.
document.addEventListener('build', function (e) {
console.log(i++);
}, false);
// Dispatch the event.
document.dispatchEvent(event);
document.dispatchEvent(event);
JSFiddle (watch console)
Information about JavaScript inheritance
Information about JavaScript event loop
Related
I'm working on the "Approve All" button. The process here is when I click "Approve All," each individual "Approve" button will be triggered as "click" all at once, and then it will send POST requests to the controller. However, when I clicked Approve All button, there was a race condition causing the controller returns Error 500: Internal server error. I have tried using JS setTimeout() with value 1500*iter, but when the iterator gets higher, for example at i = 100, then it would take 1500*100 => 150000ms (150s). I hope that explains the problem clearly. Is there a way to prevent such a case?
Here is my code, I'm using JQuery:
let inspection = $this.parents("li").find("ul button.approve"); // this will get all 'approve' button to be clicked at once
inspection.each((i,e)=>{
(function () {
setTimeout(function () {
$(e).data("note",r);
$(e).click();
}, 1500 * i); // this acts like a queue, but when i > 100, it takes even longer to send POST requests.
})(this,i,e,r);
});
// then, each iteration will send a POST request to the controller.
$("#data-inspection ul button.approve").on("click", function() {
// send POST requests
});
Any help would be much appreciated. Thank you.
That 500 error may also be the server crashing from being unable to process all the requests simultaneously.
What I'd recommend is using an event-driven approach instead of setTimeout. Your 1500ms is basically a guess - you don't know whether clicks will happen too quickly, or if you'll leave users waiting unnecessarily.
I'll demonstrate without jQuery how to do it, and leave the jQuery implementation up to you:
// use a .js- class to target buttons your buttons directly,
// simplifying your selectors, and making them DOM agnostic
const buttonEls = document.querySelectorAll('.js-my-button');
const buttonsContainer = document.querySelector('.js-buttons-container');
const startRequestsEvent = new CustomEvent('customrequestsuccess');
// convert the DOMCollection to an array when passing it in
const handleRequestSuccess = dispatchNextClickFactory([...buttonEls]);
buttonsContainer.addEventListener('click', handleButtonClick);
buttonsContainer.addEventListener(
'customrequestsuccess',
handleRequestSuccess
);
// start the requests by dispatching the event buttonsContainer
// is listening for
buttonsContainer.dispatchEvent(startRequestsEvent);
// This function is a closure:
// - it accepts an argument
// - it returns a new function (the actual event listener)
// - the returned function has access to the variables defined
// in its outer scope
// Note that we don't care what elements are passed in - all we
// know is that we have a list of elements
function dispatchNextClickFactory(elements) {
let pendingElements = [...elements];
function dispatchNextClick() {
// get the first element that hasn't been clicked
const element = pendingElements.find(Boolean);
if (element) {
const clickEvent = new MouseEvent('click', {bubbles: true});
// dispatch a click on the element
element.dispatchEvent(clickEvent);
// remove the element from the pending elements
pendingElements = pendingElements.filter((_, i) => i > 0);
}
}
return dispatchNextClick;
}
// use event delegation to mitigate adding n number of listeners to
// n number of buttons - attach to a common parent
function handleButtonClick(event => {
const {target} = event
if (target.classList.contains('js-my-button')) {
fetch(myUrl)
.then(() => {
// dispatch event to DOM indicating request is complete when the
// request succeeds
const completeEvent = new CustomEvent('customrequestsuccess');
target.dispatchEvent(completeEvent);
})
}
})
There are a number of improvements that can be made here, but the main ideas here are that:
one should avoid magic numbers - we don't know how slowly or quickly requests are going to be processed
requests are asynchronous - we can determine explicitly when they succeed or fail
DOM events are powerful
when a DOM event is handled, we do something with the event
when some event happens that we want other things to know about, we can dispatch custom events. We can attach as many handlers to as many elements as we want for each event we dispatch - it's just an event, and any element may do anything with that event. e.g. we could make every element in the DOM flash if we wanted to by attaching a listener to every element for a specific event
Note: this code is untested
I'm trying to consume sse response in javascript. The onopen and onerror works well but onmessage not.
var conn = new EventSource(`${config.serverHost}/log/stream/${this.streamId}`)
conn.onopen = (evt) => {
console.log('connected to ' + logSourceId)
}
conn.onmessage = (evt) => {
console.log(evt)
}
conn.onerror = (evt) => {
console.error(evt)
}
Connection establishes successfully and events received can be found in Chrome Network Recording as following image shown. But onmessage is never triggered!
Any comment will be appreciated.
You appear to be using named events (something along the lines of "log streami...").
To support these, you need to use the addEventListener format.
// assuming the event name is "log streaming"
conn.addEventListener("log streaming", e => {
console.log("log streaming", e)
})
FYI, onmessage is for any un-named events, aka those event streams without an event property.
event
A string identifying the type of event described. If this is specified, an event will be dispatched on the browser to the listener for the specified event name; the website source code should use addEventListener() to listen for named events. The onmessage handler is called if no event name is specified for a message.
Within my MVC 5 application, I am setting up a Signal R connection on the client end upon page load, this works as expected.
At some point later on I want add an additional handler and make a server side call, I can see that the server recieves this call which then initiates some client side calls, the handlers at the client don't get invoked.
Connection setup upon page load
function initialiseRealTimeDataRetrieval() {
var hub = $.connection.autoGeneratedProxyForHub;
hub.client.recieveRealTimeData = function (data) {
//Do Stuff
};
$.connection.hub.start().done(function () {
hub.server.getRealTimeData();
});
}
Additional calls made later on
function initialiseFeed () {
var hub = $.connection.autoGeneratedProxyForHub;
hub.client.recieveRealTimeDataFeed = function (data) {
//Do stuff
};
if ($.connection.hub.state == $.connection.connectionState.connected) {
hub.server.getRealTimeDataFeed();
}
else {
$.connection.hub.start().done(function () {
hub.server.getRealTimeDataFeed();
});
}
}
So far I have tried the following:
Made sure that calls made from the client to server are being invoked on the server.
Made sure that the additional calls are work as expected if they were made along with the calls and handlers executing upon page load.
Reviewd documentation to see if a connection must be restarted to register the new handlers.
Attempted various methods of restarting the connection after new handlers were added
The below works as expected for the additional calls however makes everything done for the connection upon page load redundant:
function initialiseFeed () {
var hub = $.connection.autoGeneratedProxyForHub;
hub.client.recieveRealTimeDataFeed = function (data) {
//Do stuff
};
$.connection.hub.stop();
$.connection.hub.start().done(function () {
hub.server.getRealTimeDataFeed();
});
}
Inspecting the hub object through the debugger does show that all clients are connected, including the additional ones.
According to the Signal R JS API Docs, the automaically generated proxy for the hub can't be used to register multiple event handler:
When to use the generated proxy
If you want to register multiple event handlers for a client method
that the server calls, you can't use the generated proxy. Otherwise,
you can choose to use the generated proxy or not based on your coding
preference. If you choose not to use it, you don't have to reference
the "signalr/hubs" URL in a script element in your client code.
Also to register new handlers for an existing connection, that connection must have at least one handler associated with it prior to establishing a connection, upon registering new handlers you must call start():
Note
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. One reason for this is that there can be many Hubs in an
application, but you wouldn't want to trigger the OnConnected event on
every Hub if you are only going to use to one of them. When the
connection is established, the presence of a client method on a Hub's
proxy is what tells SignalR to trigger the OnConnected event. If you
don't register any event handlers before calling the start method, you
will be able to invoke methods on the Hub, but the Hub's OnConnected
method won't be called and no client methods will be invoked from the
server.
I'm using Socket.IO like in this sample:
io.sockets.on("connection", function (socket) {
myService.on("myevent", function() {
socket.emit("myevent", { /* ... */ });
// some stuff happens here of course
});
});
myService is a singleton and a subclass of EventEmitter which triggers the myevent over the time. Anything works fine, however I guess that I create some kind of leak in this case. How does my service know that it doesn't need to call the handler once the connection is destroyed? Is there some kind of destroy event I can catch and then remove the handler from myService?
Listen to the socket disconnect event and when you get a disconnect event, remove the relevant event handler from the myService object.
You should be able to do that like this:
io.sockets.on("connection", function (socket) {
function handler() {
socket.emit("myevent", { /* ... */ });
// some stuff happens here of course
}
myService.on("myevent", handler);
socket.on("disconnect", function() {
myService.removeListener("myevent", handler);
});
});
If what you're trying to do is to broadcast to all connected sockets, you could just install one "myevent" listener (not one per connection) and use io.emit() to broadcast to all sockets too and not have to handle the connect or disconnect events for this purpose.
If you are planning to send data to all sockets when some other event fires, you don't need to add/remove another listeners every time a client connects/disconnects.
It is a lot more efficient and easier to simply fire the socket.io event to all sockets that are connected right now using io.sockets (which is a reference to the default namespace with all clients on it by default) and io.sockets.emit:
myService.on('myevent', () => {
io.sockets.emit('myevent', {/*...*/});
});
If you only need to fire this event to some subset of your users, try using specific namespaces or rooms:
myService.on('myevent', () => {
//with namespaces
io.of('namespace').emit('myevent', {/*...*/});
//with rooms
io.to('room').emit('myevent', {/*...*/});
});
I wonder what is a good pattern to use when you could have multiple xmlhttprequests that are part of different processes like (check login, fetch tooltip and display, show sub records/open details).
Your input on my code so far is more than welcome so are some good articles for reference on asynchronyous handling of processes.
Here is what I got so far trying to use a mediator and trying to define a sequence of events to be triggered by the mediator and initiated by a worker for a certain process
var mediator={
events:[],
// bind functions to events, optionally once only if this function doesn't
// need to handle the event during the lifetime of the application
addListener:function(name,processor,onceOnly){
if(!mediator.events[name]){
mediator.events[name]=new Array({
processor:processor,
once: onceOnly ? true : false
});
return;
}
mediator.events[name].push({
processor:processor,
once: onceOnly ? true : false
});
},
trigger:function(name,data){
var i=0;//check if mediator[name] exist
for(i=0;i<mediator.events[name].length;i++){
try{
mediator.events[name][i].processor(data);
// problem is when a use once handler is the 3rd in the chain and the
// second handler fails then the 3rd is never removed
// could trigger an error here that has a cleaner listner
}finally{
if(mediator.events[name][i].once){
mediator.remove(name,mediator.events[name][i]);
}
}
}
},
// removing listener from event
remove:function(name,event){
for(var i=0;i<mediator.events[name].length;i++){
if(mediator.events[name][i]==event){
mediator.events[name].splice(i,1);
return;
}
}
},
// used to provide an event chain through data that will execute a certain
// process
triggerNext:function(data){
// some checks on data
mediator.trigger(data.events[data.index++],data);
}
}
// possible response parsers
var parser=function(type){
var parseLogin=function(data){
console.log(data);
// should call triggerNext here for the worker to be notified.
}
if(type=="loginParser"){
return parseLogin;
}
}
// connects and triggers next
var connector=function(){
this.response="";
this.commObject=null;
this.connect=function(data){
$.get(data.url, function(res) {
data.commObject=this;//maybe you'd like to inpect it
data.response=res;
mediator.triggerNext(data);
});//trigger fail event if failed
};
}
// example of initiating a process
$("document").ready(function(){
//add all listeners that are used during the entire execution
// of the application here
var p=parser("loginParser");
mediator.addListener("checkLogin",p);
//the following is a temporary listener, this code would be in
// a worker object initLogin function.
var c=new connector();
mediator.addListener("connect",c.connect,true);
// the data determines what process will be invoked
// this could be in a worker.initLogin function
var data={
processType:"SendLoginAndCheck",
url:"test.html",
post:"",//could check in the connector.connect to see if post is set
events:["connect","checkLogin"],
//there is no worker.afterLogin but the 3rd event could be finishprocess
//and a worker object's function can be called to process that
index:0
}
//start the process
mediator.triggerNext(data);
});