How to handle all WebSocket errors without a race? - javascript

When I run code
var ws = new WebSocket("wss://mydomain.example/socket/service");
ws.addEventListener("error", function (error)
{
console.error("Got error=", error);
});
Is it possible that the WebSocket connection fails (emit error) before I can attach the event listener for the error event?
Looking at the documentation https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket I cannot see this detail documented anywhere.
According to the WHATWG spec it seems that the constructor should run the request in parallel – is there a guarantee that I can attach the error listener before any possible errors can raise?

The WebSocket constructor is run without synchronization of any kind and the connection may indeed encounter an error before the line with ws.addEventListener("error", ...); is executed! However, this is not a problem because the spec also says that in case of error, the actual error event is fired as a part of steps that must be queued as a task. In practice, this means that logically the WebSocket constructor is required to behave as if it would run an anonymous function with zero timeout which fires the error event.
So the actual error can happen before the JS code can attach the event listener but all the events (open, close, error, message) can only be fired delayed after the event loop is executed next time so the above code will always have to time attach the event handlers before the events can be fired.
See https://github.com/whatwg/websockets/issues/13#issuecomment-1039442142 for details.

Related

Clarification of when the child process' error event fires

The documentation for a child process' error event says the following:
The 'error' event is emitted whenever:
The process could not be spawned, or
The process could not be killed, or
Sending a message to the child process failed.
See also subprocess.kill() and subprocess.send().
The first case is met presumably when the cp.spawn method fails to spawn the child process successfully.
Is the bit at the bottom suggesting that case 2 and 3 can only be met when the kill and send methods fail? For instance, if the child process fails to die by other means (like when calling process.kill), the error event would not be raised. It seems that that would be the case, but I want to confirm.
If I'm never calling kill or send, can I safely not consider those cases?
My suspicion was correct (I am fairly certain) as how would the OS be able to hook back into Node's code to raise the right event?
Searching for emit('error', in Node's child_process source shows you that it can be raised by cp.spawn, kill, send and disconnect.
If the process is killed by some other means, then that event could not get raised.

How can I guarantee that websocket onopen to be called

I use WebSocket in javascript. WebSocket requires the url as constructor parameter and immediately try to connect. I can set onopen method only after construct it.
So, if WebSocket already establishes the connection before I set onopen, then I miss onopen event!
How can I avoid this?
To simulate it:
A)
1) In Chrome, open websocket.
2) Press F12 to open Developer Tools.
3) Open Console
4) Copy and paste all of these codes at once! Enter!
uri = "ws://echo.websocket.org?encoding=text";
websocket = new WebSocket(uri);
websocket.onopen = function(evt) { console.log('EHE')};
B)
Repeat 1-2-3
4) Copy and paste these codes and run
uri = "ws://echo.websocket.org?encoding=text";
websocket = new WebSocket(uri);
5) Wait a second
6) Run this code:
websocket.onopen = function(evt) { console.log('EHE')};
Result:
In A) onopen is called. In B) we missed it!
Because of the single-threaded event driven nature of Javascript, what you describe will not happen in real code. The "open" event can't be triggered until after your current section of Javascript finishes. Thus, you will always be able to set your onopen event handler before the event occurs.
Inserting artificial pauses in the debugger or in the console is an artificial situation that does not occur in real code.
What happens in real code is this:
You call new WebSocket(uri)
The webSocket intrastructure initiates a webSocket connection (an asynchronous operation)
The webSocket constructor returns immediately before that connection has completed.
The rest of your code runs (including assigning the .onopen property and setting up your event handler.
The Javascript interpreter is done running your code and returns back to the event loop.
If, by now, the webSocket has connected, then there will be an open event in the event queue and Javascript will trigger that event, resulting in your .onopen handler getting called.
If the open event has not yet completed, Javascript will wait for the next event to be inserted into the event queue and will run it, repeating that process over and over. Eventually one of these events will be your open event.
The key to this is that .onopen is called via an asynchronous event. Thus, it has to go through the Javascript event queue. And, no events from the event queue can be run until after your current section of Javascript finishes and returns back to the interpreter. That's how the "event-driven" nature of Javascript works. So, because of that architecture, you cannot miss the onopen event as long as you install your .onopen handler in the same section of Javascript that calls the constructor.
If it provides you any comfort, there are dozens of APIs in node.js that all rely on this same concept. For example when you create a file stream with fs.createReadStream(filename), you have to create the stream, then add event handlers (one of those event handlers is for an open event). The same logic applies there. Because of the event-driven nature of Javascript, there is no race condition. The open event or the error event cannot be triggered before the rest of your Javascript runs so you always have an opportunity to get your event handlers installed before they could get called.
In cases where errors could be detected synchronously (like a bad filename or bad uri) and could trigger an error event immediately, then code using something like setImmediate(function() { /* send error event here*/ }) to make sure the error event is not triggered until after your code has a chance to install your event handlers.

When does Phoenix's Socket.js fire the onConnError vs the onError callbacks?

I'm writing an application that uses the Phoenix channels and phoenix's socket.js. I want to handle errors when the websocket can't connect (spotty Internet connection, etc.) and show an appropriate message.
Looking at the socket.js source code, there are two possible ways to register for errors on the Socket object. The first is
socket.onConnError(callback)
and the second is
socket.onError(callback)
I can trigger onError to be called by stopping the Phoenix server and trying to call socket.connect() in a browser. I can't seem to get socket.onConnError to fire though - when is it used? What are the differences between these two events/callbacks?
onConnError is not a way to register a callback for an error. onConnError is used to trigger an error. Calling onConnError will, in addition to some other things, call all the onError callbacks with the specified error message. It's called by the Socket class itself on any errors thrown by the connection object.
Looking more closely at the source code, looks like onConnError is used internally for actually triggering the onError callbacks that the user has registered.
Short answer: Use socket.onError.

Marking an 'unhandled' web worker exception as handled

I am trying to test some code that uses web workers. I want to check that the error path works; i.e., that an onError handler correctly recovers from an exception thrown in the web worker.
The problem I'm running into is that the exception propagating out of the web worker causes it to be considered unhandled. For example, it prints in the browser's console log and causes my testing environment (a simple wrapper around Karma) to consider the test as failed.
So, How do I indicate to the browser/Karma that an exception bubbling out of a given web worker is expected, and should not be considered unhandled? I don't want it to print to the console, and I want my test to pass.
The only idea I've come up with is to wrap the web worker code in a try/catch and marshal the caught exception out via postMessage, but that requires throwing away quite a lot of information because of the need to stringify the error object (otherwise it triggers a data clone error).
Call preventDefault on the error event object given to the onError handler.
worker.onError = function(e) {
e.preventDefault(); // <-- "Hey browser, I handled it!"
...
}

duplicates in socket.io w/ angular

Using angular and socket.io I am getting duplicate events on the client everytime the server emits. Angular is only included once, there is only one socket.io connection, and there is only one listener per event on the client. Upon receiving an event on the server, data is logged, and this process only ever happens once. Then the data is emitted and the callback is called twice on the client, despite only being in scope once(to my knowledge).
client:
//inside a controller
var thing ='foo';
socket.emit('sentUp',thing)
socket.on('sentDown',function(thing){
console.log(thing)//this happens twice
});
server:
/*
node&express stuff here
*/
socket.on('connection',function(socket){
socket.on('sentUp',function(stuff){
console.log('this happened')//x1
socket.emit('sendDown',stuff);
});
})
Most likely your controllers are being loaded more than once. You can easily check it by logging.
Move out the socket code from the controllers and put them in a service where they're only called once.
I have found in my own socket.io client code that some of the connect events can occur each time the client reconnects. So, if the connected is lost for any reason and then the client automatically reconnects, the client may get a connect event again.
If, like me, you're adding your event handlers in the 'connect' event, then you may be accidentially adding multiple event handlers for the same event and thus you would think you were seeing duplicate data. You don't show that part of your client code so I don't know you're doing it that way, but this is an issue that hit me and it is a natural way to do things.
If that is what is happening to you, there are a couple possible work-arounds:
You can add your event handlers outside the connect event. You don't have to wait for connection to finish before adding event handlers. This way, you'd only ever do them once.
Before adding the event handlers you add upon connection, you can remove any previous event handlers that were installed upon connection to make sure you never get dups.

Categories

Resources