Sharing fetch handler logic defined across multiple service workers - javascript

Following This Discussion, Where there is a comment that speaks of
patching a Fetch
by overriding self.fetch, self.XMLHttpRequest, and self.caches (for
cache.add/addAll)? It would seem these would let you intercept network requests and manipulate responses before the main SW script sees them.
I have been looking out for any documentation that speaks of such stuff, and can not seem to find any.
In a scenario where I simply need multiple service workers to co exist in a single scope,
After having an importScripts in one of them to import the event handlers of the other,
How exactly would I patch a fetch / avoid a fetch race / have both the fetch handlers work?

There's a few things to cover here:
Multiple Service Workers in a Single Scope
There can only be one active service worker for a given scope. If you attempt to register two different service worker scripts that each have the same scope, the second registration will trigger the service worker update flow:
// There's an implied default scope of '/'.
// See https://stackoverflow.com/a/33881341/385997
navigator.serviceWorker.register('/sw1.js');
// If called later on, this will trigger the update flow.
// You'll only end up with one of the two being active.
navigator.serviceWorker.register('/sw2.js');
The exact timing for when sw2.js will activate and take control over any existing clients depends on whether you're using self.skipWaiting() and self.clients.claim() inside of sw2.js. Once sw2.js activates, sw1.js will be marked as redundant.
Another way of asking what I think is the same question is whether you can have multiple service workers controlling the same client page at the same time. The answer is no, you can have at most one service worker controlling any client page, and only that service worker will be able to respond to fetch events originating from the page.
Using importScripts to Share Common Handlers
Instead of attempting to register multiple service workers with the same scope, using importScripts() to pull in logic that's defined in a different JavaScript file sounds like a reasonable approach. There are a few things to keep in mind when using importScripts() in this fashion:
importScripts() needs to be called during the initial startup execution of your service worker code, not inside an event handler. I.e. "lazy-loading" of importScripts() is not supported.
importScripts() executes all the of the code inside of the file(s) synchronously, one by one, in the order in which they're listed. You can have multiple importScripts(), or importScripts() inside of files that are themselves imported, and they'll all execute in a defined order.
Inside an imported script, self will be set to the same ServiceWorkerGlobalScope that would be used if the code were in the top-level service worker. I.e., there's no difference between calling self.addEventListener() inside of an imported script or inside of the top-level service worker.
(This isn't directly related to your question, but it's good to know:) The files referenced via importScripts() will be cached by default, using the same mechanism that's built in to the browser for caching your top-level service worker file. While there are some changes to the service worker specification underway to change this, as of right now, those cached importScripts() files will be used indefinitely as long as their filenames don't change. So a best practice is to either include a version number or a hash in the file names of anything referenced with importScripts().
Multiple fetch Event Handlers
What happens when you have multiple calls to self.addEventListener('fetch')?
From the previous section we know that it's not relevant whether those multiple calls originate inside of an importScripts() resource or the top-level service worker. They both operate on the same global scope.
The behavior is well-defined: when a client page makes a request, it will trigger the fetch handlers of the controlling service worker one by one, in the order in which they were registered, until the first call is made to event.respondWith(). One one fetch event handler calls respondWith(), no other fetch event handlers will be triggered, and it's the sole responsibility of that handler to (eventually) return a Response to the client page.
Since the order in which your self.addEventlistener('fetch') calls matter, make sure that you list the files in your importScripts() in an appropriate order, and either include your call to importScripts() before or after you define any fetch event handlers in your top-level service worker, depending on which you want to take precedence.
While you can use conditional logic to determine whether or not to call event.respondWith(), that logic can't be asynchronous, since the service worker won't wait to see whether event.respondWith() is called. It needs to synchronously move on to the next event handler (assuming there is one).
So inside a fetch handler, you can use conditional logic like
// This can be executed synchronously.
if (event.request.url.endsWith('.html')) {
event.respondWith(...);
}
but you can't use conditional logic like:
// caches.match() is asynchronous, and the service worker will have
// moved on to the next `fetch` handler before it completes.
caches.match('index.html').then(response => {
if (response) {
event.respondWith(...);
}
});
There's a live code sample that you can explore if you want to see the multiple-handlers behavior for yourself.

Related

Angular/RxJS: Should I put all my Subscriptions in a unique function or method of my Service?

Recently I have been debugging my Angular code in which I have distributed several subscriptions to various public functions of my Service, and I have seen that when these functions are invoked both from within and from outside the Service, the debugger returns to execute all the code that I have inside the declaration of each subscription.
Until now I thought that this code was only executed once (the first one) and then it remained in memory to be invoked by events .next() for example.
Does this mean that it would be logical to concentrate all my .subscribe() instructions on a single "registry function" that is only executed once?
Are infinite copies of my subscriptions being created in memory without me knowing?
I do not want an answer of "depends on the type of subscriber" because I do not think that as programmers we should know the implementation of each external resource that we use... I am finding the safest and robust way of doing it always.
Thanks in advance.
Every subscription to your observable creates its own execution context.
// one execution context created
yourObservable.subscribe(x=>{})
// another execution context created
yourObservable.subscribe(x=>{})
If you do not want every execution context for each subscription, you need to share():
yourSharedObservable = yourObservable.share();
//only one execution context
yourSharedObservable.subscribe(x=>{})
yourSharedObservable.subscribe(x=>{})
If you want yourSharedObservable to cache the data and share it between all subscribers, you may shareReplay():
yourSharedAndCachedObservable = yourObservable.shareReplay(1);

If Call js file as async function undefined in javascript [duplicate]

If you use plain script tags on an HTML page, rendering is blocked until the script has been downloaded and parsed. To avoid that, for faster page display, you can add the 'async' attribute, which tells the browser to continue processing down the page without waiting for that script. However, that inherently means that other javascript that refers to anything in that script will probably crash, because the objects it requires don't exist yet.
As far as I know, there's no allScriptsLoaded event you can tie into, so I'm looking for ways to simulate one.
I'm aware of the following strategies to defer running other code until an async script is available:
For a single script, use their 'onload' event or attribute. However, there's no built-in way I know of to tell when ALL scripts have loaded if there's more than one.
Run all dependent code in onload event handlers attached to the window. However, those wait for all images too, not just all scripts, so the run later than would be ideal.
Use a loader library to load all scripts; those typically provide for a callback to run when everything has loaded. Downside (besides needing a library to do this, which has to load early), is that all code has to wrapped in a (typically anonymous) function that you pass into the loader library. That's as opposed to just creating a function that runs when my mythical allScriptsLoaded fires.
Am I missing something, or is that the state of the art?
The best you could hope for would be to know if there are any outstanding async calls (XMLHttpRequest, setTimeout, setInterval, SetImmediate, process.nextTick, Promise), and wait for there to not be one. However, that is an implementation detail that is lost to the underlying native code--javascript only has its own event loop, and async calls are passed off to the native code, if I understand it correctly. On top of that, you don't have access to the event loop. You can only insert, you can't read or control flow (unless you're in io.js and feeling frisky).
The way to simulate one would be to track your script calls yourself, and call after all script are complete. (i.e., track every time you insert a relevant script into the event loop.)
But yeah, the DOM doesn't provide a NoAsyncPending global or something, which is what you'd really require.

Instance Initializers in Emberjs

We are getting through the long slog of updating our ember-cli application to its latest iteration. We fell very much behind. I am at the stage where instance initializers have been introduced and I am getting the feeling this is going to break the way in which I have implemented a certain initializer currently.
export function initialize(container, application) {
var store = container.lookup('store:main');
// We need a basket to be present when
// the application loads. Wait for this
// to happen before continuing.
application.deferReadiness();
store.findOrCreateRecord('order', basketToken).then(function(basket) {
container.register('basket:main', basket, { instantiate: false });
application.inject('controller:basket', 'model', 'basket:main');
// Let the application know we have
// a basket and can continue.
application.advanceReadiness();
});
}
What is now recommended is that I split this up into a "normal" initializer to register the basket object and an instance initializer to grab the store and make the call to our API server. Doing this however I would not have access to the registry within the instance initializer to register the returned object from my promise which I would then inject into my controller. I assume I am thinking about this all wrong, but I have not been able to wrap my head around it. Any suggestions how I should by updating this?
I think it's reasonable to post #tomdale explanation here as an answer to help others with understanding initializers.
#tomdale: "It's not possible to defer app readiness in an instance initializer, since by definition instance initializers are only run after the app has finished booting.
Sidebar on the semantics of application booting: "App readiness" (as in, deferReadiness() and advanceReadiness()) refers to whether all of the code for the application has loaded. Once all of the code has loaded, a new instance is created, which is your application.
To restate, the lifecycle of an Ember application running in the browser is:
Ember loads.
You create an Ember.Application instance global (e.g.
App).
At this point, none of your classes have been loaded yet.
As your JavaScript file is evaluated, you register classes on the
application (e.g. App.MyController = Ember.Controller.extend(…);)
Ember waits for DOM ready to ensure that all of your JavaScript
included via <script> tags has loaded.
Initializers are run.
If you need to lazily load code or wait for additional setup, you can call deferReadiness().
Once everything is loaded, you can call advanceReadiness().
At this point, we say that the Application is
ready; in other words, we have told Ember that all of the classes
(components, routes, controllers, etc.) that make up the app are
loaded.
A new instance of the application is created, and instance
initializers are run.
Routing starts and the UI is rendered to the
screen.
If you want to delay showing the UI because there is some runtime setup you need to do (for example, you want to open a WebSocket before the app starts running), the correct solution is to use the beforeModel/model/afterModel hooks in the ApplicationRoute. All of these hooks allow you to return a promise that will prevent child routes from being evaluated until they resolve.
Using deferReadiness() in an initializer is an unfortunate hack that many people have come to rely on. I call it a hack because, unlike the model promise chain in the router, it breaks things like error and loading substates. By blocking rendering in initializers, IMO you are creating a worse experience for users because they will not see a loading or error substate if the promise is slow or rejects, and most of the code I've seen doesn't have any error handling code at all. This leads to apps that can break with just a blank white screen and no indication to the user that something bad has happened."

Javascript: Is 'require' synchronous method in AMD (asynchronous module definition)?

Is 'require' synchronous in AMD (asynchronous module definition)? If so, what makes this specification asynchronous? What if I have require() (and it hasn't been loaded yet) in the middle of my code, will it stall execution? Talking browser-side.
There are two different synchronous concepts here.
The first is "Will it stop my entire webpage, and sit and wait for the file.".
The answer is no. RequireJS doesn't do that if you've got a script with dependencies.
If you use it appropriately, it uses a promise-system.
What that means is that if you send in your callback and define your requirements for that file, the callback won't be run until all of the required files are loaded.
If there's a require inside of one of those required files, then THAT callback won't be run until ITS dependencies have loaded.
The outermost callback (the one that would be at the bottom of your script, normally), won't run until everything inside has.
This works on a promise system.
It's worth understanding how promise systems work (similar to an observer-pattern, in a way).
They're meant to be passed around or chained, based on an event, rather than having multiple people listen in any order.
var widget = new Widget(),
widgetLoaded = widget.load(url); // return a promise to let the program use the widget
widgetLoaded.then(function () { widget.move(35); })
.then(function () { widget.setColour("Blue"); })
.then(function () { widget.show(); });
This is like returning this so that you can chain function calls, except that the calls don't actually happen until widget.load() completes.
The widget will actually control when this happens, by keeping its promise if the widget loads and everything is fine, or by breaking its promise if something went wrong.
In most promise systems, .then or whatever they call it, either takes two functions (kept and broken -- in my systems, brokens are always optional), or they take an object with success and failure -- $.ajax does this, and then lets you predetermine what you want to do with the data when it's loaded, or if it fails -- promises.
So your page still work 100% asynchronously (without interrupting the UI), but it's 100% synchronous in that all of the modules will fire in the right order.
One thing you MUST REMEMBER:
If you have these dependencies in your code, you can not have any dependencies lying around at the bottom of your script, waiting to run, inline.
They must all be locked away inside of your callback, or locked inside a function waiting to be called by your callback.
This is simply because it is an asynchronous process, in terms of actual processing, and will not block the browser from running events/JS, rendering the page, et cetera.
For requireJS:
You have to pass a callback method alongside the required modules to .require(), that will get fired when the resources were loaded successfully. So, of course you should/can only access loaded AMD or CommonJS modules just within that callback.
for NodeJS:
Yes, .require() does work synchronously. NodeJS uses the CommonJS module system, not AMD.

Notifying JavaScript of external event from XPcom

I have been trying to find a solution to what seems to be relatively simple scenario. I have JavaScript running in an html page that makes a call to an XPcom that I have written in C++. So far, so good.
This XPcom retrieves a reference to the observer-service (do_GetService("#mozilla.org/observer-service;1")); fires a notify which is 'seen' by the JavaScript; the XPcom creates a windows thread (AFX), passes a copy of the observer-service reference and returns to the JavaScript caller; expecting the XPcom thread to send additional notifies at appropriate times (based on external events).
The notifies however fail to arrive in the JavaScript and my initial thought was the notify method will not deliver notifications from a 'different' thread. I've used the VStudio debugger to confirm the program sequence is as expected; ie the external event is being received by the thread and the notify method is being called... but, no notify event arrives.
I've read quite a few postings across the web and nothing really 'nails' the particular scenario I'm trying to address. I'm not married to the idea of using notify:
I've tried event notification via NS_DispatchToCurrentThread however that is not working out either because I don't have an "event" from the JavaScript side to deliver. I can create one of my own within the context of the XPcom and I can 'notify it'; but, that was just a POC to prove I could deliver events from XPcom; now I need for the JavaScript side to give me an event to notify;
I've tried passing a new'ed object as a nsiSupports arg; but, the DispatchToCurrentThread wants an nsiRunnable and I cannot figure out how to pass one of those (the idl file does not support);
I've also considered 'wrapping' the event with some sort of object that is compatible with nsiSupports; but, am unsure about the details of doing so.
The objective is quite simple. deliver asynchronous events or notifications from an XPcom thread to the main, or even sub, thread of the JavaScript; but, I'm getting less than 10% traction here.
Has anyone accomplished this, or have ideas as to how to get it done?
You are correct, the observer service works only on the main thread (check the return value of your Notify() call, it should be NS_ERROR_UNEXPECTED). You are also correct as far as the solution goes - dispatch an event to the main thread, via NS_DispatchToMainThread(). As to the actual implementation, function NS_NewRunnableMethod() should help (unless you want to create your custom nsIRunnable implementation - e.g. to pass parameters). You probably want to do something like this:
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(xpcomComponent, &nsMyComponent::processEvent);
nsresult rv = NS_DispatchToMainThread(event);
Here xpcomComponent would be a reference to your XPCOM component instance and nsMyComponent::processEvent its method that needs to be called on main thread. That method would then be able to notify the observer.

Categories