javascript - threads - ¿Cómo hacer que EventSource esté disponible dentro de SharedWorker en Firefox?
web workers html5 ejemplos (1)
Estoy tratando de implementar eventos enviados por el servidor (SSE) dentro de SharedWorker.
La implementación funciona sin problemas en Google Chrome. Sin embargo, no funciona en Firefox en absoluto.
Cuando intento que funcione en Firefox, aparece este error en la consola.
error {
target: SharedWorker,
isTrusted: true,
message: "ReferenceError: EventSource is not defined",
filename: "https://example.com/add-ons/icws/js/worker.js",
lineno: 28,
colno: 0,
currentTarget: SharedWorker,
eventPhase: 2,
bubbles: false,
cancelable: true,
defaultPrevented: false
}
¿Cómo puedo hacer que EventSource
esté disponible dentro de SharedWorker
?
Así es como establezco la conexión the SharedWorker
$(window).load(function(){
//establish connection to the shared worker
var worker = new SharedWorker("/add-ons/icws/js/worker.js" );
//listen for a message send from the worker
worker.port.addEventListener("message",
function(event) {
console.log( Math.random() );
processServerData(event.data);
}
, false
);
worker.onerror = function(event){
console.log(event);
};
//start the connection to the shared worker
worker.port.start();
});
Este es mi script de trabajador
var clients = new Array();
readNewMessages();
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
clients.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
replyToClientMessage(event, port);
} , false
);
}
//reply to any message sent to the SharedWorker
replyToClientMessage = function (event, port) {
port.postMessage(event.data);
}
//runs every time and post the message to all the connected client
function readNewMessages(){
var serv = new EventSource(''/add-ons/icws/poll.php'');
serv.addEventListener("getMessagingQueue", function(event) {
var queue = JSON.parse(event.data);
notifyAllPorts(queue);
}, false);
}
//check all open clients and post a message to each
function notifyAllPorts(msg){
var len = clients.length;
var port;
for(i = 0; i < len; i++) {
port = clients[i];
port.postMessage(msg);
}
}
Mientras buscaba una solución, descubrí que EventSource
no forma parte de SharedWorkerGlobalScope
Traté de cambiar mi código de trabajador a esto, pero aún así eso no funcionó
var serv = new self.EventSource(''/add-ons/icws/poll.php'');
var clients = new Array();
readNewMessages();
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
clients.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
replyToClientMessage(event, port);
} , false
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
replyToClientMessage = function (event, port) {
port.postMessage(event.data);
}
//runs every time and post the message to all the connected client
function readNewMessages(){
serv.addEventListener("getMessagingQueue", function(event) {
var queue = JSON.parse(event.data);
notifyAllPorts(queue);
}, false);
}
//check all open clients and post a message to each
function notifyAllPorts(msg){
var len = clients.length;
var port;
for(i = 0; i < len; i++) {
port = clients[i];
port.postMessage(msg);
}
}
¿Cómo puede solucionar este problema?
Por qué FF le permitiría tener un WebSocket en un Worker pero no en un EventSource, no estoy seguro, pero le da todas las herramientas para hacer un buen polyfill (pegarlo en la parte superior de su script de SharedWorker):
//FF only; some missing functionality, but handles the essentials
//most of what''s missing can be added if you have the motivation
(function(global) {
if (''EventSource'' in global)
return;
function EventSource(url) {
if (!(this instanceof EventSource))
return new EventSource(url);
this.url = url;
var self = this;
var listeners = {};
self.addEventListener = function(type, handler) {
if (!listeners[type]) {
listeners[type] = new Set();
}
listeners[type].add(handler);
};
self.removeEventListener = function(type, handler) {
if (listeners[type]) {
listeners[type].delete(handler);
}
};
self.dispatchEvent = function(event) {
if (listeners[event.type]) {
listeners[event.type].forEach(function(handler) {
setTimeout(function() {
switch (typeof(handler)) {
case ''object'':
handler.handleEvent(event);
break;
case ''function'':
handler(event);
break;
}
});
});
}
if (typeof(self[''on'' + event.type.toLowerCase()]) == ''function'') {
setTimeout(function() {
self[''on'' + event.type.toLowerCase()](event);
});
}
};
var buffer = '''';
//if you want to handle other prefixes, you''ll need to tweak these
var msgRE = /^(?:data: .*/n)*/n/;
var dataRE = /^data: (.*)$/;
function _parse() {
while (msgRE.test(buffer)) {
var msg = msgRE.exec(buffer)[0]; //msg now contains a single raw message
var data = null;
var lines = msg.split("/n").slice(0, -2); //remove last 2 newlines
if (lines.length) {
data = '''';
lines.forEach(function(line) {
data += dataRE.exec(line)[1];
});
}
var event = new MessageEvent(''message'', { ''data'' : data, ''origin'' : url });
self.dispatchEvent(event);
buffer = buffer.substr(msg.length);
}
}
var xhr = new XMLHttpRequest();
xhr.open(''GET'', url, true);
xhr.responseType = ''moz-chunked-text''; //FF only
xhr.setRequestHeader(''Accept'', ''text/event-stream'');
xhr.onprogress = function() {
if (xhr.response !== null) {
buffer += xhr.response;
}
_parse();
};
xhr.onreadystatechange = function() {
switch (xhr.readyState) {
case XMLHttpRequest.HEADERS_RECEIVED:
if (xhr.status == 200) {
self.readyState = EventSource.OPEN;
break;
} //else
console.error("EventSource: " + url + " = " + xhr.statusText);
//fallthrough
case XMLHttpRequest.DONE:
self.readyState = EventSource.CLOSED;
break;
default:
break;
}
};
xhr.send();
Object.defineProperty(this, ''close'', { ''value'' : function() {
xhr.abort();
}});
return this;
}
Object.defineProperties(EventSource, {
''CONNECTING'' : { ''value'' : 0, ''enumerable'' : true },
''OPEN'' : { ''value'' : 1, ''enumerable'' : true },
''CLOSED'' : { ''value'' : 2, ''enumerable'' : true },
});
EventSource.prototype = Object.create(EventTarget.prototype);
Object.defineProperties(EventSource.prototype, {
''constructor'' : { ''value'' : EventSource },
''readyState'' : { ''value'' : 0, ''writable'' : true, ''enumerable'' : true },
''withCredentials'' : { ''value'' : false, ''enumerable'' : true }, //not supported
''onopen'' : { ''writable'' : true },
''onmessage'' : { ''writable'' : true },
''onerror'' : { ''writable'' : true },
''close'' : { ''value'' : function() { }, ''configurable'' : true, ''enumerable'' : true }
});
global.EventSource = EventSource;
})(this);
Puede encontrar más rellenos completos aquí y aquí . Necesitaba uno que funcione con una transmisión en caché en tiempo real (si no está conectado a la transmisión cuando ocurre el evento, se va); esto es lo que se me ocurrió La principal diferencia es el tipo de respuesta moz-chunked-text, que le da acceso a la secuencia de almacenamiento en memoria caché en el evento de progreso. Disfruta ;-)