I am new to webworker but I managed to send xmlhttprequest to my rest api and I got json back. But I want send this request again and again (in a loop), until the page is active.
I actually want to show values in real time. I want to make a simple web application in which when data is inserted in database my webworker should show that data without refreshing the page.
Is there any better way to do so. Kindly help me in it.
sorry for bad English.
You can use EventSource to get stream from server until .close() is called at Worker, or message is passed to Worker signalling Worker to call .close().
const es = new EventSource("/path/to/server");
es.addEventListener("open", function(event) {
console.log("event source open")
});
es.addEventListener("message", function(event) {
// do stuff with `event.data`
console.log(event.data);
});
es.addEventListener("error", function(event) {
console.log("event source error", event)
});
button.addEventListener("click", function() {
es.close();
});
Related
i have a service, which uses QWebSocketServer. My server is able to handle client requests and send several events to client (without any request, it is important). I've tested my server with QWebSocket class like shown in Qt example. All works perfectly.
Now i want to implement frontend in js. And i've faced with one thing which i cannot understand. If client sends request to server, server's answer can be received on client side, but if server sends data without client request, i'm not able to receive this data.
In my js script i have usual code:
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
So, why i can't receive data from server without request and what should i do for getting the ability to receive that?
server has been sending messages from another thread, that was the cause of problem
I have a logging API in which is executed before a link. The link will be redirecting the user to other place and I'm executing fetch before the user is redirected. So the script is like this now:
loggingAPI({
timestamp: moment()
})
window.location = "http://.......com"
The logging api is just a normal fetch wrapper.
However, the server doesn't receive the API request right now. I think it's because of it doesn't even get the chance to send the request to the api.
So can I wait for the request to be sent but not waiting for the response?
Using sendBeacon it's very simple
without seeing the code for you function loggingAPI the following is a best guess
Note: sendBeacon uses a POST request, so the server side may need to be modified to accept such a request - though, seeing as your loggingAPI is sending data, I imagine it is already using POST - so this may be a non-issue
somewhere in your code, set up an unload event for windows
window.addEventListener("unload", () => {
sendBeacon("same url as loggingAPI", JSON.stringify({timestamp: moment()}));
}, false);
Then, when you
window.location = "http://.......com"
the loggingAPI function gets called for you
edit: sorry, I didn't flesh out the code fully, I missed a few steps!!
You can send the request in a service worker.
https://developers.google.com/web/fundamentals/primers/service-workers/
Here's some fetch specific information:
https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent
You would register the service worker, and then send a message to it before redirecting.
The upside to the initial complexity is that once you start using service workers, they open up a whole new world of programming; You will end up using them for much more then queuing up messages to send.
Step 1 Register a service worker
index.html
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('service-worker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
Step 2 Create the service worker script
service-worker.js
self.addEventListener('install', function(e) {
return Promise.resolve(null)
});
Step 3 Create a listener in server worker script
service-worker.js
self.addEventListener('message', function (event) {
console.log('message', event.data)
// call fetch here, catching and responding to what you stashed in the message
});
Step 4 Send the message before you redirect
index.html
Just a demo to simulate your client.
setTimeout(() => {
navigator.serviceWorker.controller.postMessage({message: 'A LOG MESSAGE'});
}, 2000)
After you put all pieces in place, MAKE SURE YOU CLOSE ALL TABS AND REOPEN, or have chrome dev tools set up to deal with refreshing the worker.
An old question, but if you're using fetch you can use the keepalive flag.
The keepalive option can be used to allow the request to outlive the page. Fetch with the keepalive flag is a replacement for the Navigator.sendBeacon() API.
https://developer.mozilla.org/en-US/docs/Web/API/fetch#keepalive
I have recently been working on a project with both a client (in the browser) and node.js. I am using web sockets to communicate between the two. I have implemented an API on my server that communicates over the WebSockets and the server works just fine. In the browser, I am implementing the following class (in javascript) to interface with the API.
class ApiHandlerV1 {
constructor(apiUrl) {
//create the object
this.ws = null;
}
makeRequest(request,callback) {
//make a request
}
connect() {
//connect the websocket
this.ws = new WebSocket(this.url);
this.ws.onmessage = function () {
//call callback?
}
}
}
The issue that I am caught up on is that I want to be able to call makeRequest, provide a callback, and once the socket has gotten data back trigger the callback. I have thought about just re-defining .onmessage every time that I make a request but that just seems dirty to me and there is most likely a nice and easy solution to this.
Clarifications: Because of how I implemented my server I will only get a single message back from the server.
As Dominik pointed out in the comments I should also say that I am going to call .connect() before I make a request. I will be calling makeRequest multiple times after in other parts of my code.
I'm introducing service worker on my site.And i'm using app-shell approach for responding to requests.Below is my code structure.
serviceWorker.js
self.addEventListener("fetch", function(event) {
if (requestUri.indexOf('-spid-') !== -1) {
reponsePdPage(event,requestUri);
}else{
event.respondWith(fetch(requestUri,{mode: 'no-cors'}).catch(function (error){
console.log("error in fetching => "+error);
return new Response("not found");
})
);
}
});
function reponsePdPage(event,requestUri){
var appShellResponse=appShellPro();
event.respondWith(appShellResponse); //responds with app-shell
event.waitUntil(
apiResponse(requestUri) //responds with dynamic content
);
}
function appShellPro(){
return fetch(app-shell.html);
}
function apiResponse(requestUri){
var message=['price':'12.45cr'];
self.clients.matchAll().then(function(clients){
clients.forEach(function (client) {
if(client.url == requestUri)
client.postMessage(JSON.stringify(message));
});
});
}
App-shell.html
<html>
<head>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.onmessage = function (evt) {
var message = JSON.parse(evt.data);
document.getElementById('price').innerHTML=message['price'];
}
}
</script>
</head>
<body>
<div id="price"></div>
</body>
</html>
serviceWorker.js is my only service worker file. whenever i'm getting request of -spid- in url i calls reponsePdPage function.In reponsePdPage function i'm first responding with app-shell.html. after that i'm calling apiResponse function which calls postmessage and send the dynamic data.The listener of post message is written in app-shell.html.
The issue i'm facing is, sometimes post message gets called before the listener registration.It means the apiResponse calls post message but their is not register listener to that event. So i cant capture the data.?Is their something wrong in my implementation.
I'm going to focus on just the last bit, about the communication between the service worker and the controlled page. That question is separate from many of the other details you provide, such as using PHP and adopting the App Shell model.
As you've observed, there's a race condition there, due to the fact that the code in the service worker and the parsing and execution of the HTML are performed in separate processes. I'm not surprised that the onmessage handler isn't established in the page yet at the time the service worker calls client.postMessage().
You've got a few options if you want to pass information from the service worker to controlled pages, while avoiding race conditions.
The first, and probably simplest, option is to change the direction of communication, and have the controlled page use postMessage() to send a request to the service worker, which then responds with the same information. If you take that approach, you'll be sure that the controlled page is ready for the service worker's response. There's a full example here, but here's a simplified version of the relevant bit, which uses a Promise-based wrapper to handle the asynchronous response received from the service worker:
Inside the controlled page:
function sendMessage(message) {
// Return a promise that will eventually resolve with the response.
return new Promise(function(resolve) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
resolve(event.data);
};
navigator.serviceWorker.controller.postMessage(message,
[messageChannel.port2]);
});
}
Inside the service worker:
self.addEventListener('message', function(event) {
// Check event.data to see what the message was.
// Put your response in responseMessage, then send it back:
event.ports[0].postMessage(responseMessage);
});
Other approaches include setting a value in IndexedDB inside the service worker, which is then read from the controlled page once it loads.
And finally, you could actually take the HTML you retrieve from the Cache Storage API, convert it into a string, modify that string to include the relevant information inline, and then respond with a new Response that includes the modified HTML. That's probably the most heavyweight and fragile approach, though.
i'm using WebSockets to send data from my node.js server to my clients. Since the data can be kind of large, the UI thread used to block, so no user interaction or video playing was possible during the data was received. That's when i stumbled over WebWorkers, and i also managed to get them work together with WebSockets.
app.js:
...
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false);
worker.postMessage('init');
...
worker.js:
function initWebSocket() {
var connection = new WebSocket('ws://host:port', ['soap', 'xmpp']);
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
//self.postMessage('Worker received : ' + e.data);
};
};
self.addEventListener('message', function(e) {
switch (e.data) {
case 'init':
initWebSocket();
break;
default:
self.postMessage('Unknown command: ' + e.data);
};
}, false);
All i'm doing so far is receive the data. Of course, later on i intend to do more stuff with it. But my problem is: The UI thread is still blocking when large files arrive. Did i get something wrong here?
UPDATE:
Actually, i have to revise my previous comment. Obviously chrome had cached some of my files i was sending before, so i didn't realize the problem starts already with files way smaller than 300MB (currently, i'm testing a 50MB file). The ui blocks until the file has been completly received. What i'm currently doing is the following: I'm loading an index page with a video playing. Also, on the same page, i put a button which starts a worker. The worker does send an xhr request to the server and gets a 50MB file. So i just dismissed WebSockets for the sake of it. What's happening when i click the button: The video freezes until the complete data has been received. When i do the same and let the worker just calculate numbers in a for-loop, the video keeps playing. So it seems to have something to work with using the network, but not specifically WebSockets. Is it possible that WebWorkers just can't work with network stuff?