I would like to run a web worker in the background (on mobile, it works on desktop) which does an action every minute by sending an HTTP request. Unfortunately, after having the website in the background for about 5 minutes, no HTTP request will be sent anymore... All network requests will resume once you go back to the website.
It seems like the web worker is not allowed to be run in the background, at least not on mobile, as it works fine on desktop. I could not find any source stating why it is throttled, and what can be done about it. I found a similar issue dating from 2017, which is 4 years ago and has not been answered quite the way I want it and might also be outdated.
I thought of the following solutions:
Normally in native Android you can show a notification which will ensure that your app keeps running in the background, I was wondering if this would also work on web in combination with web workers.
If you open a web socket in your web worker, will it keep your web worker alive in the background?
Progressive web app. Does not seem to keep it active unfortunately..
Is there anything I could do about this?
All modern browsers restrict background usage. It has a really simple reason: Background tasks require resources, and users dont want a million websites in the background to eat all your RAM. And malicious websites could just use the CPU from users for bitcoin mining etc.
But there is a way to do stuff in the background. You already mentioned it in the question: You need to send push notifications. You can just include a fetch() in your push notification handler.
But here's the catch: You have to send a notification every time you want your site to fetch something, or your requests will always/sometimes be blocked depending on your browser. From MDN:
Activating a service worker to deliver a push message can result in increased resource usage, particularly of the battery. Different browsers have different schemes for handling this, there is currently no standard mechanism. Firefox allows a limited number (quota) of push messages to be sent to an application, although Push messages that generate notifications are exempt from this limit. The limit is refreshed each time the site is visited. In comparison, Chrome applies no limit, but requires that every push message causes a notification to be displayed.
You just need to set up Push Notifications. There's a great guide from Google that you can follow if you don't know how to set up push notifications.
An implementation in a service worker would look like this:
self.addEventListener('push', function(event) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
return;
}
// The HTTP request
fetch("...");
var data = {};
if (event.data) {
data = event.data.json();
}
var title = data.title || "Background activity";
var message = data.message || "You can ignore this";
var icon = "images/new-notification.png";
var notification = new Notification(title, {
body: message,
tag: 'simple-push-demo-notification',
icon: icon
});
notification.addEventListener('click', function() {
if (clients.openWindow) {
clients.openWindow('https://example.blog.com/2015/03/04/something-new.html');
}
});
});
(Code copied from MDN)
Related
For an alert system following a reservation (private use), I would like my webapp to send a notification.
I want every X minutes an ajax call is made, checks if there is a new reservation and if so sends the notification.
I have no problem for the operation and the sending of the notification unless the mobile is locked.
I have seen several posts on this subject but they are dated.
I tried settimetout, setinterval and background-timer, without success.
Maybe today there is a way?
EDIT :
To be more clear. My webapp manages to send notifications even when the mobile is locked, but the verification interval seems random.
A test with a setInterval of 1 minute, sends me a notification after 2 minutes for example, then 5 minutes later... not always with the same interval.
EDIT 2 :
In response to Gowtham K K, I tried using a web worker for the setInterval but it doesn't work when mobile is locked.
Code in my main page :
if(window.Worker){
var myWorker = new Worker("sw2.js");
myWorker.postMessage(0);
myWorker.onmessage = function(e){
var dt = new Date();
var hm = dt.toISOString().substring(11, 19);
_test.innerHTML += hm+"<br>";
}
}
Code in sw2.js
self.onmessage = function(e) {
self.postMessage(0);
init();
}
function init() {
setInterval(send,60000);
}
function send() {
self.postMessage(0);
}
As far as I know, service worker would atleast might solve most of your problem.
Service worker is a background worker for a website. It is supported by most modern browsers.
It acts an proxy layer between browser and web server. For example when user laoded web app and then internet got disconnected. At that time you can register in service worker and so when user connects to internet again, you can make the service worker task to run even when the user is not seeing the webpage.
Here you can handle push notification inside service worker .
Service worker has its own lifecycle.
In your case you can try running the background timer inside the service worker and handle the push notification.
Some Useful links:
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
https://medium.com/#a7ul/beginners-guide-to-web-push-notifications-using-service-workers-cb3474a17679
I am current using Microsoft Graph API (beta) to get Presence Status e.g. Online, away etc. in an spfx webpart (using React) using GraphClient:
this.props.context.msGraphClientFactory.getClient().then(async (client: MSGraphClient) => {
let response = await client
.api('/communications/getPresencesByUserId')
.version('beta')
.post(postData)
console.log("Communication API Response: "+ response);
this.usersWithPresence = response.value;
});
This is working fine, but to get updated status of a user, I have to refresh the page so another API call is made and updated presence status. I want to do it like this happens in 'Skype'.
What I need is suggestions about a mechanism that I can apply to get
real time updates in user's presence status, so as soon as user
updates the status this is reflected in my webpart. I know I can use
setInterval or setTimeout functions to request for presence status
after specific intervals but for learning purposes i don't want to
request API this way again and again but rather getting updated
message from server like this happens using web sockets. How a web
socket sort of stuff can be applied with this API?
your suggestions are welcome.
Today this API doesn't support any kind of subscription mechanism to when the status changes. There is a uservoice entry you can upvote for it. That means the only way to get any changes is to periodically poll it. As for socket io, the only support today is for SharePoint lists, for any other resource you need to stand up your own infrastructure to relay the message.
I'm concerned about using Clients.All in my C# Signal R hub class
Clients.All.setConnectionStatus(profileId, true);
I'm calling it in both OnConnected() and OnDisconnected(bool stopCalled)
to show the online status of my logged in users.
OnDisconnected() isn't as bad because it's only being called when someone actually logs off
ex.
public override Task OnConnected()
{
string profileId = Context.User.Identity.Name;
_connections.Add(profileId, Context.ConnectionId);
Clients.All.setConnectionStatus(profileId, true);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string profileId = Context.User.Identity.Name;
_connections.Remove(profileId, Context.ConnectionId);
if (stopCalled == false) // log off only
Clients.All.setConnectionStatus(profileId, false);
return base.OnDisconnected(stopCalled);
}
My concern -> Blowing up the users machine client side with javascript code constantly running in the browser!
Scenario -> If I have > ~1000 users online and logged in traversing to different pages, I will be broadcasting to all of them a signal R connection Clients.All.setConnectionStatus(profileId, true);
which in the javascript hub looks like this below which could easily end up blowing up the users browser, because it would be constantly receiving these broadcasting connection status calls!!!
scope.chat = $.connection.chatHub;
// Receive and set the connection status
scope.chat.client.setConnectionStatus = function(profileId, isConnected) {
// look for prfileId in html
var $profileStatusElement = $(".online-status-profile[data-id='" + profileId + "']");
var $profileChatElement = $(".online-status-chat[data-id='" + profileId + "']");
if (isConnected === true) {
// change the online status
} else {
// change the online status
}
};
I have chat windows to users (online) like below that shows a users status next to their name. So I know if the person I'm sending a message to is online or not!
When I first load the chatbox, I fetch the online status server side, but after that it's up to the OnConnected() and OnDisconnected() methods in my hub to set the online status of the person I'm chatting with.
There's got to be a better way then broadcasting the signal out to 'all' online users on my site? But the only way I can think of is to poll the status every so often on some javascript timer, but I hate that idea!!
Another way would be to keep some kind of in-memory collection of who each person is chatting with, but that seems like it could get really messy and could end up up not being very accurate, being there could be so many things in between the client and server that could cause a disruption to mismanage the in-memory collection of who's connected to whom! Any thoughts or ideas here would be greatly appreciated!!
FYI -> I also have a messages page, that shows a list of all the recent conversations I've had with users like below. And on this page I show the online status of these users as well. On this page I fetch the online status of each user on the server before I render the page, same as the chat box, but once the page is loaded, it's up to the OnConnected() and OnDisconnected(bool stopCalled) to set the online status of each user on the page.
You're doing in right way with signalR. I guide you based on your concerns/scenarios:
My concern -> Blowing up the users machine client side with javascript
code constantly running in the browser!
Don't forget that you are using a browser underlying functionality (WebSocket), so we have no choice unless running some js code inside client loaded page. You take advantage of browser capabilities here.
Scenario -> If I have > ~1000 users online and logged in traversing to
different pages, I will be broadcasting to all of them a signal R
connection Clients.All.setConnectionStatus(profileId, true);
Before proposing any solution, I exhort you to employ grouping logic in your application. Grouping reduces the volume of synchronizations.
If you working on a public chat room (which can contain large number of people), by employing server side strategies simply you'll make your code to a dirty one! Yes, Any user who navigates or refreshes the page can cause a redundant update to all the clients. Best Solution is turning you page to SPA (working with API call or Ajax call) with no page refresh. I believe that any real-time web application should take advantage of modern web technologies. This will solve almost every problem which you suffer (e.g no page refresh, no reRun, no disCoennection & etc ...).
Solutions like using a js timer to fetch latest status for users is not clean or desirable.
I have built a portal which provides access to several features, including trouble ticket functionality.
The client has asked me to make trouble ticket functionality available offline. They want to be able to "check out" specific existing tickets while online, which are then accessible (view/edit) while the user's device is out-of-range of any internet connection. Also, they want the ability to create new tickets while offline. Then, when the connection is available, they will check in the changed/newly created tickets.
I have been tinkering with Service Workers and reviewing some good documentation on them, and I feel I have a basic understanding of how to cache the data.
However, since I only want to make the Ticketing portion of the portal available offline, I don't want the service worker caching or returning cached data when any other page of the portal is being accessed. All pages are in the same directory, so the service worker, once loaded, would by default intercept all requests from all pages in the portal.
How can I set up the service worker to only respond with cached data when the Tickets page is open?
Do I have to manually check the window.location value when fetch events occur? For example,
if (window.location == 'https://www.myurl.com/tickets')
{
// try to get the request from network. If successful, cache the result.
// If not successful, try returning the request from the cache.
}
else
{
// only try the network, and don't cache the result.
}
There are many supporting files that need to be loaded for the page (i.e. css files, js files, etc.) so it's not enough to simply check the request.url for the page name. Will 'window.location' be accessible in the service worker event, and is this a reasonable way to accomplish this?
Use service worker scoping
I know that you mentioned that you currently have all pages served from the same directory... but if you have any flexibility over your web app's URL structure at all, then the cleanest approach would be to serve your ticket functionality from URLs that begin with a unique path prefix (like /tickets/) and then host your service worker from /tickets/service-worker.js. The effort to reorganize your URLs may be worthwhile if it means being able to take advantage of the default service worker scoping and just not have to worry about pages outside of /tickets/ being controlled by a service worker.
Infer the referrer
There's information in this answer about determining what the referring window client URL is from within your service worker's fetch handler. You can combine that with an initial check in the fetch handler to see if it's a navigation request and use that to exit early.
const TICKETS = '/tickets';
self.addEventListener('fetch', event => {
const requestUrl = new URL(event.request.url);
if (event.request.mode === 'navigate' && requestUrl.pathname !== TICKETS) {
return;
}
const referrerUrl = ...; // See https://stackoverflow.com/questions/50045641
if (referrerUrl.pathname !== TICKETS) {
return;
}
// At this point, you know that it's either a navigation for /tickets,
// or a request for a subresource from /tickets.
});
I've been looking at the state of HTML notifications and service workers, and was wondering - is it possible to show a notification on a delay? Basically, I would like to be able to say "remind me in 30 minutes" (or whatever), then push a notification to the user 30 minutes later. That could be scheduled immediately, but I'm not seeing any functionality that allows it.
Am I missing something or is it impossible in the current state of (particularly) Chrome APIs?
This is possible but not straightforward with service workers, at least in their present form. It's not straightforward because a service worker can't keep itself awake for half an hour or wake itself up with a setTimeout or setInterval. The browser will just shut the worker down and will keep no record of any timeouts or intervals. You could wake it up with a message from an open tab, but you said that you don't want to have to have to keep an open tab, and if you assume an open tab then why even bother with the service worker anyway? As Jeff Posnick suggested in a comment, you could perhaps eventually use the Sync or PeriodicSync APIs, but, as he also points out, they aren't implemented yet and aren't really intended for this anyway.
You can accomplish what you want in current versions of Chrome using the Push API, but you'll have to include the server in the loop and set yourself up with a push notification service (i.e. GCM). Here's how it would work, roughly:
When you decide to delay a notification, let the server know about it
After a delay, the server sends out a push message for your user
The service worker is woken up in response to the push and creates a new notification
This last part will be a hassle, because currently you can't actually send any kind of payload with a push, so your service worker will need some way of figuring out what the notification is supposed to be. Maybe the server has a list of snoozed notifications and the service worker can get it from there, or maybe you saved them in IndexedDB.
Adapted from https://developer.cdn.mozilla.net/media/uploads/demos/e/l/elfoxero/c17223c414d8ddafb7808972b5617d9e/html5-notifications_1400214081_demo_package/:
<script>
var Notification = window.Notification || window.mozNotification || window.webkitNotification;
function show() {
window.setTimeout(function () {
var instance = new Notification("Hello World!");
}, 5000);
return false;
}
</script>
Notify me!