skipWaiting not working in Service Worker - service-worker

As part of writing ServiceWorker code in typescript, I am attaching install handler and then calling skipWaiting inside it:
self.addEventListener('install', this.onInstall);
protected onInstall() {
console.log('onInstall called');
workbox.skipWaiting();
}
With this, new ServiceWorker is still in waiting state and skipWaiting doesn't seem to be working. onInstall handler gets called perfectly fine.
Is typescript implementation artifact causing problem here? Like I should write something like this?
self.addEventListener('install', event => {
self.skipWaiting();
});
Or self.skipWaiting() doesn't work the same way as workbox.skipWaiting()?
Interestingly, moving workbox.skipWaiting() to ctor where install handler is being attached fixes the issue.

Related

Electron IPC behaving inconsistently

I have ipc connected and sending messages early, but when I call ipc later in onclick it is failing.
I am using ipc because I want to communicate with the file system for some settings. It works fine here in Main:
ipcMain.on("settings", (event, arg) => {
console.log("Writing muting to settings: " + arg.settings)
switch(arg.settings){
case "mute_status":
event.reply("settings_reply", settings.muted)
break;
case "mute:true":
event.reply("settings_reply", "muted set to true")
default:
event.reply('settings_reply', 'Sure thing, got it')
}
})
Renderer:
ipc.sendSync('settings',{"settings": "mute_status"})
ipc.once('settings_reply', (event, arg) => {
console.log('Muted is: ' + arg)
if(arg){
place_icon("mute", "volume_div")
document.getElementById("audio").muted = true
} else {
place_icon("volume-up", "volume_div")
}
})
When I later make another call it works sometimes(almost always if I restart Windows). It either works or doesn't with each run of the program. The second call connects to the same in main.js with this in the renderer:
ipc.sendSync("settings", {"settings": "mute:" + muted})
ipc.once('settings_reply', (event, arg) => {
console.log("We're talking")
} )
I've tried quite a few things and I'm happy to provide more info if needed. What is causing this? How do I fix it?
You are using ipcRenderer.once(channel, listener). Its purpose is to register a one-time event handler which will be removed after it is triggered. Change it to ipcRenderer.on(channel, listener)
ipcRenderer.once(channel, listener)
• channel String
• listener Function
event IpcRendererEvent
...args any[]
Adds a one time listener function for the event. This listener is
invoked only the next time a message is sent to channel, after which
it is removed.

Difference between .then() and .whenCompleted() methods when working with Futures?

I'm exploring Futures in Dart, and I'm confused about these two methods that Future offers, .then() and .whenCompleted(). What's the main difference between them?
Lets say I want to read a .txt using .readAsString(), I would do it like this:
void main(){
File file = new File('text.txt');
Future content = file.readAsString();
content.then((data) {
print(content);
});
}
So .then() is like a callback that fires a function once the Future is completed.
But I see there is also .whenComplete() that can also fire a function once Future completes. Something like this :
void main(){
File file = new File('text.txt');
Future content = file.readAsString();
content.whenComplete(() {
print("Completed");
});
}
The difference I see here is that .then() has access to data that was returned!
What is .whenCompleted() used for? When should we choose one over the other?
.whenComplete will fire a function either when the Future completes with an error or not, instead .then will fire a function after the Future completes without an error.
Quote from the .whenComplete API DOC
This is the asynchronous equivalent of a "finally" block.
then runs if the future completes successfully.
catchError runs if the future fails.
whenComplete runs regardless of the future completed with a value or with an error.
Here's the basic flow:
someFuture().then((value) {
print('Future finished successfully i.e. without error');
}).catchError((error) {
print('Future finished with error');
}).whenComplete(() {
print('Either of then or catchError has run at this point');
});
.whenComplete = The function inside .whenComplete is called when this future completes, whether it does so with a value or with an error.
.then = Returns a new Future which is completed with the result of the call to onValue (if this future completes with a value) or to onError (if this future completes with an error)
Read detail on API DOC
whenComplete then

Why Stop method is invoked many times in Topshelf

Topshelf is working as the windows service broker in our application. This morning, we find that the Stop mehtod is invoked many times. Here is the related code.
class Program
{
static void Main(string[] args)
{
ILog Log = new FileLog();
try
{
HostFactory.Run(serviceConfig =>
{
serviceConfig.Service<ServiceManager>(serviceInstance =>
{
serviceInstance.ConstructUsing(() => new ServiceManager());
serviceInstance.WhenStarted(execute => execute.Start());
serviceInstance.WhenStopped(execute => execute.Stop());
});
});
}
catch (Exception ex)
{
Console.WriteLine(ex);
Log.Error("Program.Main", ex, LogType.Error);
Console.ReadLine();
};
}
}
In the ServiceManager, we have the Stop mehtod, which will be invoked then TopShelf receives the stop signal from operation system.
class ServiceManager
{
xxx.....
public bool Stop()
{
try
{
_log.Info("The service is stopping", LogType.Info);
_service.StopExecuteProduceMessage();
Task.WaitAll(_tasks.ToArray());
_log.Info("The service is stopped", LogType.Info);
}
catch (Exception ex)
{
_log.Error("Stop", ex, LogType.Error);
}
return true;
}
}
This morning, we find the service is stopped with unclear reason. And there are many lines logging about this stop action.
I guess Topshelf invoke ServiceManager.Stop method many times. Anyone have encountered this problem before? I want to know I can trace why this happens.
Anyone can help? Many thanks.
You are experiencing this behavior because your Stop() method takes a while but is not being responsive to the request to stop.
Your method essentially looks like this:
Stop() {
log-stopping;
wait-a-while;
log-stopped;
}
While you are waiting, the status of the service remains "Running". This causes the requester (could be Windows itself or another program) to keep re-sending the stop request, resulting in multiple parallel/overlapping calls to Stop(). That accounts for the first 10 lines in the log you included.
You can see that it takes almost 20 seconds for the "wait" to complete (from 05:39:45 to 05:40:04).
After that, it looks like Topshelf may be stuck. That causes more messages to be sent. (Notice that in the next lines in your log, the stopping and starting pairs are logged simultaneously because your tasks are stopped and there is no waiting).
To fix the problem, you should:
Modify your WhenStopped() call to pass the HostControl parameter to Stop():
serviceInstance.WhenStopped((execute, hostControl) => execute.Stop(hostControl));
Update the Stop() method to take the HostControl parameter and make this call before the call to Task.WaitAll():
hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30));
This will inform Windows that your service has received the request and may be working on it for up to 30 seconds. That should avoid the repeated calls.
Reference: Topshelf documentation

How to wait for xtext service to complete before executing code

I am using the Xtext web editor framework and am having an issue with code being executed before its preconditions have been run. For instance, when I update my document It calls into the XtextServlet doService method. I have subclassed the XtextServlet class and overridden the doService method like this:
override doService(XtextServiceDispatcher.ServiceDescriptor service, HttpServletResponse response) {
super.doService(service, response)
switch (service.context.getParameter("serviceType")) {
case "update": {
// Execute other code
}
}
This other code that I am running relies on the document having been updated. But I have found that sometimes the document is not updated before my code runs. I assume these services are spun up in their own threads then. My question is, is there a way to trigger my code to run as a result of the service actually completing or do I need to completely override the update service to call my code?
EDIT: To clarify my question, where I call super.doService(service, response), this executes an edit onto the document. Where I have my comment // Execute other code I am trying to run some other side effect for the service. This other code relies on the doService method to have finished, but doService spins off an async task which is not finishing before my other code executes

ServiceWorkerRegistration.active not being set first time (Chrome)

On Chrome Mac. I am trying to register a ServiceWorker and set a variable to it. When I call register() and the service worker has not previously been installed, the "active" property seems to be set to null immediately and then get initialized (asynchronously?) very soon after.
var sw = null;
navigator.serviceWorker.register('preview/sw.js', {scope: 'preview/'}).
then(function(registration) {
console.dir(registration);
sw = registration.active;
if (!sw) {
console.log('wat');
console.dir(registration);
}
});
In other words, I get into the if-block the first time the service worker has been installed. The console shows the active property as being set equal to the ServiceWorker in both console.dir() commands, yet the sw variable is null.
Refreshing the page fixes the problem. Anybody know what could be causing this?
For the first visit you're describing, the registration is not yet active when that promise resolves but it is "installing", so the registration's installing property will return a service worker.
Since no service worker is in the waiting state, it will then transition to activating then active. So you're right in that registration property is not initially active but on refresh, it will be.
The following code will illustrate:
navigator.serviceWorker.register('/serviceworker.js').then(onRegistration);
function onRegistration(registration) {
if (registration.waiting) {
console.log('waiting', registration.waiting);
registration.waiting.addEventListener('statechange', onStateChange('waiting'));
}
if (registration.installing) {
console.log('installing', registration.installing);
registration.installing.addEventListener('statechange', onStateChange('installing'));
}
if (registration.active) {
console.log('active', registration.active);
registration.active.addEventListener('statechange', onStateChange('active'));
}
}
function onStateChange(from) {
return function(e) {
console.log('statechange initial state ', from, 'changed to', e.target.state);
}
}
On first visit, the console.log output would be:
installing ServiceWorker {scriptURL: "http://...", state: "installing", onstatechange: null, onerror: null}
statechange initial state installing changed to installed
statechange initial state installing changed to activating
statechange initial state installing changed to activated
The state changes happen asynchronously as you observed.
The service worker is registered, but it isn't active yet 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.

Resources