Server Side Event - onmessage not firing - javascript

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.

Related

How does stream.Readable.prototype.on handle events in node.js source code?

I was confused when reading code of stream.Readable in Node.js.
here is source code:
https://github.com/nodejs/node/blob/master/lib/_stream_readable.js#L778-L799
Readable.prototype.on = function(ev, fn) {
const res = Stream.prototype.on.call(this, ev, fn);
if (ev === 'data') {
// Start flowing on next tick if stream isn't explicitly paused
if (this._readableState.flowing !== false)
this.resume();
} else if (ev === 'readable') {
const state = this._readableState;
if (!state.endEmitted && !state.readableListening) {
state.readableListening = state.needReadable = true;
state.emittedReadable = false;
if (!state.reading) {
process.nextTick(nReadingNextTick, this);
} else if (state.length) {
emitReadable(this);
}
}
}
return res;
};
Obviously, the if statements only handle data and Readable event, but according to the API document, the on method of stream.readable also accept other events such as close , end , error.
So my question is :
According to the source code, how did stream.Readable handle other events except data and readable?
What you are seeing here is an override for the .on() method so that the Readable class can watch what event listeners are being attached and can do something special when someone installs a listener for the data event or the readable event.
The first line of this function:
const res = Stream.prototype.on.call(this, ev, fn);
is where the Readable passes the callback and event name arguments to its parent so that the normal implementation will be run. A Stream implements the EventEmitter interface so calling the super method with Stream.prototype.on.call(this, ev, fn) will give .on() it's expected default behavior.
Then after calling the parent, it checks to see if the event that someone is listening to is the data event or readable event and then implements a little extra functionality when one of those event listeners is attached.
For the data event, it resumes the stream so that it will start flowing if it was paused and if it was set to flowing mode. This is probably because when a Readable is being initially created and configured, if it starts flowing the stream before the data event listener is attached, then data on the stream could be missed. So, it doesn't start flowing until someone is around to listen to data events.
Note, there are potentially lots of over events that occur on the stream and those are all handled by the call to the base class in the first line. What you are seeing here is just some special behavior that the Readable class wants to implement when two specific event listeners are first added. This code does not affect when those events are sent or how they are listened to. It just triggers a little behavior in the Readable state when a listener for one of these events is first attached.

indexedDb no events fire on open

I am seeing that sometimes neither of the events are being fired when doing indexedDb.open().
If I set a timeout and observe the state of openRequest, the ready state is set = 'done'. If I do any transaction on the db in the openRequest.result, it works fine.
My guess is that in some cases the openRequest execution is complete before we get to attach onsuccess or other event handlers, in other words it does not get executed in the next event loop.
So, I can inspect the state after an 'x' amount of time if no event is fired. However this approach is hackish and fragile.
Does anyone know of a better way to address this?
var db;
var openRequest = window.indexedDB.open("todos", 1);
openRequest.onerror = function(event) {
console.error('error in open', event);
};
openRequest.onsuccess = (event) => {
console.log('success ' , event)
};
openRequest.onupgradeneeded = (event) => {
console.log('upgradeneeded ' , event);
db = event.target.result;
db.createObjectStore("toDoList", { keyPath: "taskTitle" });
};
openRequest.onblocked = (event) => {
console.log('blocked ' , event);
};
setTimeout( () => {
console.log('timeout');
console.log(openRequest.readyState) // equals done
}, 10000)
Either a "success" or an "error" event must fire when readyState becomes "done". If that's not happening then you've found a browser bug.
As noted in a comment, you'll want db = event.target.result in the onsuccess handler as well, otherwise db will not be set if an upgrade was not necessary. Are you certain that this isn't the source of your error? (i.e. maybe success was firing, you just weren't capturing the result?)
in some cases the openRequest execution is complete before we get to attach 'onsuccess' or other event handlers
If that happened it would be a browser bug. Are you seeing consistent behavior across browsers? Can you reliable reproduce this?

Making custom events on webworkers

I made a webworker and tried to add a custom event called progress as follows:
self.updateProgress = function(no){
let event = new CustomEvent(
'progress',
{
detail : {
description: "event from webworker to update progress",
timeofevent: new Date(),
eventcode: 15
},
bubbles: true,
cancelable: false,
data : no
});
self.console.log(self.dispatchEvent(event));
};
On the parent JavaScript:
scope.worker.addEventListener('progress', (e) => { //cannot add custom events to worker yet
scope.progress = e.data;
});
The problem is that the event is never triggered, and from what I have read, it seems that message and error are the only events that are attached to the worker object. Is it impossible to add custom events?
You set worker.addEventListener('message', (e) => {}) in main thread, but you don't self.dispatchEvent at worker. Instead, you call self.postMessage(obj, []); and worker implementation does the job (passing data between threads and firing events).
Proto of WebWorker is EventTarget in Chrome, so you probably could use it as event target where you create it. But worker in main thread and self in worker are not the same object / scope, so you dispatch event and listen event on different targets.
But nothing prevents you from using standard self.postMessage way for keeping track of loading progress.

document.open removes all window listeners

I'm writing a Chrome extension. It's used for recording users' behavior on browsing web pages. It does that by adding event listeners to customers' web pages, using Chrome content script.
Code in content script looks like:
var recordingEvents = ['click', 'input', 'change'];
recordingEvents.forEach(function (e) {
window.addEventListener(e, handler, true);
});
Example of custom page:
<script>
function reload() {
var ifrw = document.getElementById("iframeResult").contentWindow;
ifrw.document.open();
ifrw.document.write("<div>abc</div>");
ifrw.document.close();
}
</script>
<body>
<input type="submit" onclick="reload();" value="Reload" />
<iframe id="iframeResult"></iframe>
</body>
It uses document.open, document.write to rewrite content of iframe.
Here is the question. My event listeners are attached to window object. And document.open removes all its event listeners. Like picture below shows.
Is there a way to avoid document.open removing event listeners? Or to observe document.open, so I can manually re-add listeners after it?
I've found this issue trying to solve exactly the same problem.
Here is a spec https://html.spec.whatwg.org/multipage/webappapis.html#dom-document-open that says that on document.open current document is destroyed and replaced with a fresh one. I had a hope that some event's like "load" are still preserved, no luck.
Here is my detection code:
const testEventName = 'TestEvent';
let tm;
function onTestEvent() {
clearTimeout(tm);
}
function listenToTestEvent() {
document.addEventListener(testEventName, onTestEvent);
}
listenToTestEvent();
function onLostEvents() {
console.log('events are lost');
listenToTestEvent();
// DO THING HERE
}
function checkIfEventsAreLost() {
document.dispatchEvent(new CustomEvent(testEventName));
tm = setTimeout(onLostEvents);
}
new MutationObserver(checkIfEventsAreLost).observe(document, { childList: true });
When document is recreated its childList is changed(new documentElementnode), this is the best trigger I've thought of to detect document replacement.
Note that even listeners fire before setTimeout(..., 0)
This is a detailed explanation of why #Viller's answer works. I'm making this a new answer as it didn't fit into a comment
The TestEvent event is a special event that monitors when the events that were previously setup in a document are removed.
In particular, this accounts for the case of document.open, which removes all listeners not only from the document but also from the window.
The general idea is to setup a listener for a custom event called TestEvent, which clears a timeout. Such timeout is setup only when the document mutates and is triggered by a mutation observer.
Since the timeout schedules the operation to happen at least during the next tick of the event loop, such timeout can be cleared before that, avoiding the execution of its callback all together. And, since the TestEvent event handler clears that timeout, the fact that the timeout is cleared implies that the listener is still attached. On the other hand, if the timeout is not cleared before the next tick, the would signify the events were removed and a new "setup" is needed.
According to MDN:
The Document.open() method [...] come(s) with some side effects. For example:
All event listeners currently registered on the document, nodes inside
the document, or the document's window are removed.
Below I provide a module (onGlobalListenerRemoval) where one can easily register some callback functions to get notified whenever listeners get cleared. This uses the same working principle as the code in Viller's answer.
Usage principle:
onGlobalListenerRemoval.addListener(() => {
alert("All event listeners got removed!")
});
Module code:
const onGlobalListenerRemoval = (() => {
const callbacks = new Set();
const eventName = "listenerStillAttached";
window.addEventListener(eventName, _handleListenerStillAttached);
new MutationObserver((entries) => {
const documentReplaced = entries.some(entry =>
Array.from(entry.addedNodes).includes(document.documentElement)
);
if (documentReplaced) {
const timeoutId = setTimeout(_handleListenerDetached);
window.dispatchEvent(new CustomEvent(eventName, {detail: timeoutId}));
}
}).observe(document, { childList: true });
function _handleListenerDetached() {
// reattach event listener
window.addEventListener(eventName, _handleListenerStillAttached);
// run registered callbacks
callbacks.forEach((callback) => callback());
}
function _handleListenerStillAttached(event) {
clearTimeout(event.detail);
}
return {
addListener: c => void callbacks.add(c),
hasListener: c => callbacks.has(c),
removeListener: c => callbacks.delete(c)
}
})();

Nodejs JavaScript Events

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

Categories