How to run function in background with service worker - javascript

I am building a javascript Web Application. In there i want to upload pictures and other data to a server. This is all working, but i want to use the application offline as well. Therefore i implemented the indexedDB, to store the data offline. Furthermore i made a sync-function, which will be executed every 5 seconds (setIntervall()...) to syncronise the data to the server if there is a internet connection again.
The problem is, that this sync-function only works when the app is open. So I researched a method to solve my problem and i got the idea of a "service worker" with background sync.
The problem is i don't know how to implement it.
This is what i currently have (the whole app is programmed in MVC-concept):
I have a sw.js with the following code:
if (event.tag == 'myFirstSync') {
event.waitUntil(sync());
}
});
In the controller there is the function sync() for synchronising the data

You can implement your background sync inside of your sw.js file, in the handler for the 'sync' event:
self.addEventListener('sync', function(event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(() => {
// Put your sync code here.
});
}
});
At some point after you register your service worker, you need to trigger a sync:
// Register your service worker:
navigator.serviceWorker.register('/sw.js');
// Then later, request a one-off sync:
navigator.serviceWorker.ready.then(function(swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
After triggering the sync once, if it fails, the sync should keep on retrying on its own (even if the app is not open in the browser).
If it fails, another sync will be scheduled to retry. Retry syncs also
wait for connectivity, and employ an exponential back-off.
See https://developers.google.com/web/updates/2015/12/background-sync for further details.

Related

How to enable sync manager in electron?

I am building an offline cross-platform using electron. It uses a service worker to make the app offline first. When I try to register the sync manager through following command:-
swRegistration.sync.register('myFirstSync')
Expected behavior
On the page with a service worker registered, this snippet should produce no errors.
navigator.serviceWorker.ready.then(function(swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
Actual behavior
When running with an electron, I get
Uncaught (in promise) DOMException: Background Sync is disabled.
I need to enable sync manager. Any idea of how it can be done?
According to developers.google.com
You just have to follow this article, which is on their website and you can see it on the site by clicking here.
This is the article:
How to request a background sync
In true extensible web style, this is a low level feature that gives you the freedom to do what you need. You ask for an event to be fired when the user has connectivity, which is immediate if the user already has connectivity. Then, you listen for that event and do whatever you need to do.
Like push messaging, it uses a service worker as the event target, which enables it to work when the page isn’t open. To begin, register for a sync from a page:
//register your service worker:
navigator.serviceWorker.register('/sw.js')
//then, later, request a one-off sync:
navigator.serviceWorker.ready.then(function(swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
Then listen for the event in /sw.js:
self.addEventListener('sync', function(event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
And that's it! In the above, doSomeStuff() should return a promise indicating the success/failure of whatever it’s trying to do. If it fulfills, the sync is complete. If it fails, another sync will be scheduled to retry. Retry syncs also wait for connectivity, and employ an exponential back-off.
The tag name of the sync ('myFirstSync' in the above example) should be unique for a given sync. If you register for a sync using the same tag as a pending sync, it coalesces with the existing sync. That means you can register for an "clear-outbox" sync every time the user sends a message, but if they send 5 messages while offline, you'll only get one sync when they become online. If you want 5 separate sync events, just use unique tags!

Angular and Server Sent Events: close method call does not close the data stream

Tech stack: Angular 7, Spring webflux (with spring boot), Chrome browser
I cannot share the actual code due to policy restrictions. Appreciate your understanding.
I have followed this example: https://medium.com/#chrisbautistaaa/server-sent-events-in-angular-node-908830cc29aa
I have a boolean variable makeCall acting as a condition switch for my if else
When user clicks a button on screen toggleSseCall method containing the below logic is called in Angular component
Body of toggleSseCall method:
makeCall: boolean = true;
toggleSseCall() {
let eventSource = new EventSource(url);
if(makeCall) {
Call service method: getServerSentEvents(eventSource) //similar to the example in link one difference being i'm passing the Instantiated eventSource reference
Subscribe to the observable returned by the above call like in the example and console log the data
} else { //When user clicks the button again, call goes here as makeCall is false
//Call flow comes here as expected, I verified that with console logging
eventSource.close(); //I verified to make sure the close method is not uppercase or anything like that
}
makeCall = !makeCall //toggle boolean flag
}
Server side:
Just RESTFul Get Api call that returns Flux and the Flux just returns hello string every x seconds
The call is happening and the "hello" text streams from back-end to front-end as expected. But, the data stream doesn’t stop on call to close method. I want the data stream to close when user clicks the button again. Would appreciate any suggestions on this.
A bit late, but I just came across this issue myself. For me, this only happens when developing locally via ng serve, using the Webpack dev server to proxy requests to my backend app.
The proxy is implemented using the NPM package http-proxy-middleware. There's
an unresolved issue suggesting this is a bug.
When the browser connects directly to the backend API, this issue doesn't happen for me.

FCM push notification sent when browser is closed and not showing when it is reopened

Web push notification is working fine when browser is open. But with browser closed not sure if notifications are queuing up or not as when its reopened the notification is not showing.
I tried to set the TTL but its still not working so I think the notifications are not getting queued when browser is closed.
You can make use of Web Periodic Background Sync API You can run this feature offline too and even app is closed, it doesn't require backend server for push as it triggers by the service worker periodically (min 24 hr I guess)
in your project files register sync event:
registration.periodicSync.register(constants.periodicBgSyncEventName, {
minInterval: syncMinInterval,
networkState: "any",
});
in your service worker file listen to the sync and show web notification:
self.addEventListener("periodicsync", (event) => {
if (event.tag === constants.periodicBgSyncEventName) {
self.registration.showNotification("Wake Time !!!", {
body: `Hi, Good Morning`,
});
}
});
Note - In order to use this Periodic Sync API you need to install the PWA first.

Is it reliable to unregister service worker on window.unload event?

I am using something like this to unregister my service worker:
window.addEventListener("unload", () =>{
registration.unregister().then(function(successs) {
if(successs){
console.log("Unregister succesfull!");
}
else {
console.log("unregister unsuccessfull!!");
}
});
});
Since the unregister function returns a promise, it seems that the unregister process is asnyc. So is it reliable to do this unregistration on the unload event?
I have tested on Chrome and it works, but can't find anything in the spec or anywhere that can validate whether what I am doing will always work.
MORE INFO
I know that it seems weird unregistering the service worker on document unload. But I am not using the service worker for caching or notification or other PWA stuff. I am using to serve a dummy HTML page that I generate at runtime. So I just open an iframe with some dummy src and start intercepting all the requests from that iframe from the service worker. I want this service worker to be unregistered after the user closes the tab.
There is a working example that can be found via this Look at the RegisterServiceWorker.js file that is called from TravelManager.html.
I don't get any console output after the call so assume the promise is not resolved? Either way what does it do? IMHO the registration of a Service Worker (intrinsically for many clients/pages) should not be bound to one page.
The manifest registration/declaration functionality seems to be there?
What do you want the unregister to do?
I wouldn't tie it to an event on one page, otherwise having multiple tabs open of the same page will be hard to handle. Personally I'd have the service worker query for clients with setInterval and if the number of clients drops to zero, unregister itself (with self.registration.unregister()).
Edit: it turns out the above doesn't work due to web workers getting eventually killed. I've ended up using the above strategy but instead of setInterval, every time the site is closed it sends a message to the service worker which then checks how many clients it still has.

unable to post message to service worker because controller value is null

I am trying to make a website available offline with the help of service workers to cache the files needed by the page.
I am trying to give the user control over which images he wishes to cache.
For this, I am using a function sendMessage
function sendMessage(msg){
navigator.serviceWorker.controller.postMessage(msg);
}
where message contains data relative to the image that the user wishes to cache or un-cache.
The service worker contains an event listener for the message event:
this.addEventListener('message', function(event){
var data = event.data;
caches.open('v1').then(function(cache){
if(data.add==1)
return cache.add(data.url);
else
cache.delete(data.url).then(function(response)
{
console.log(response)});
console.log(event);
}
)})
The problem I am facing is that the controller is always null.
The problem is that your service worker is registered, but it isn't active and it isn't controlling your page yet.
If you want your service worker to become active, you can call the function skipWaiting in the install event handler.
If you want your service worker to control the page as soon as it becomes active, you can use the Clients.claim function in the activate event handler.
You can see an example in the ServiceWorker Cookbook Immediate Claim recipe.

Categories