-
Notifications
You must be signed in to change notification settings - Fork 312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify dynamic-imported scripts are never installed #1356
Comments
Can we make dynamic My impression is no one has implemented or shipped es modules on service workers yet, so hopefully we have a lot of leeway to make changes here. |
It seems bad to make dynamic |
I seem to recall the driving motivation was to avoid people building service workers that functioned when online and then unexpectedly broke offline. The sync'ness of importScripts was not really a factor. We wanted to remove the footgun by fast failing. |
I think it was in part the synchronous nature as it would effectively block the event loop. |
Ok. I guess I don't recall that. I don't think the fact Or are you saying import() resources should be offlined whenever its used? I guess someone needs to actually write a PR to make https://w3c.github.io/ServiceWorker/#importscripts Of course, maybe I don't understand the module stuff since I don't see where Run Service Worker uses https://w3c.github.io/ServiceWorker/#run-service-worker-algorithm Sorry if I'm just confused. |
Yes, I think import() should be treated symmetrically to fetch(), most likely. Both are ways of dynamically fetching resources at runtime, which will behave in predictable ways (e.g. fail unless cached) when offline. |
I'm confused what this means here. fetch() in a SW bypasses the SW and has no awareness of the |
F2F: There was some discussion of options:
It turned out Chrome is the only browser with immediate plans to implement module service workers. There was rough consensus that throwing on dynamic imports for now is the safe thing to do, with room to change later as we get real usage of module service workers and understand the problem more. Todo:
|
Note that |
We didn't discuss that, and I didn't know that. But yes, I think we'd block dynamic imports from ServiceWorkerGlobalScope regardless of whether it's a module or classic service worker. |
Chromium issue: https://bugs.chromium.org/p/chromium/issues/detail?id=900045 |
Why not just try and throw if the network request fails? (keeping in mind all the existing limitations for loading scripts async in SW via From a developer perspective, the primary use case I can think of for dynamic But in the install event, you're most likely to have network, so it's unlikely to be an issue. The other concern I have about always failing is it'd make it much harder to generically share code between the window and the SW. Realistically, though, I can't think of many cases where the code I'd want to share would require dynamic |
It sounds like this is option: "Make dynamic imports just like fetch(): no installation, no bouncing through FetchEvent". The cons with that included:
|
Oh, I wasn't meaning to imply that I didn't want dynamic import to go through a SW FetchEvent. I think I'd prefer that behavior (even though it is a bit weird). My reasoning is that I'd expect the same response if I dynamic-imported a URL from both the window and the SW (which I imagine may be somewhat common in the future, e.g. an IndexedDB helper library). If the SW responded with something different, I think that'd be unexpected. That being said, I can see how you have the potential to get into an infinite loop if the FetchEvent logic can use a dynamic import. Maybe disallow dynamic import in a FetchEvent? |
Would it be possible to disallow dynamic import fetching triggered from FetchEvent, but allow dynamic imports that resolve to already-instantiated modules? |
Seems possible, but what does that solve? |
I'm sorry to piggy back on this, but I'm having trouble with service worker (workbox) when offline - but I'm using fetch() and import() from the main UI thread (I wondered if one or two of the comments on this issue are thinking they are being called in UI thread, while this is, I think, talking specifically about them being called from the service worker itself). |
This comment has been minimized.
This comment has been minimized.
If we're saying that import('data:text/javascript;charset=utf-8,export default 1'); There's no network dependency here, but it feels like rejecting is right, as it provides a feature detect for the rest of |
Yeah, we should expand the check for Worklet to also cover service workers in https://html.spec.whatwg.org/#hostimportmoduledynamically(referencingscriptormodule,-specifier,-promisecapability). That would suffice I think. |
Ohhh yes! I didn't realise that worklets have a similar behaviour here. That makes things a lot simpler. |
https://github.com/web-platform-tests/wpt/blob/master/service-workers/service-worker/import-module-scripts.https.html - looks like we need to update this so failure is expected. We should also test:
It isn't clear to me how the latter should behave, but we should match what currently happens in worklets. Edit: It rejects before the module registry is checked, so importing something already statically imported will fail. |
#developer-pain Like Hugo i have also recently adopted a fetch + eval using shimport - hate to use eval but that's what you got to do. feels unnecessary to boot up the hole application when only parts of the service worker is being used. It also makes the hole thing "slower" when you have to initiate everything Not being able to write cross platform independent code for main thread nodejs and service worker and deno is a developer pain Would be grate if "Allow fetches of dynamic imports to go to the service worker's own FetchEvent to enable offline." Beside service worker are not only made for offline experiences, ppl build service worker for other reasons that otherwise require a internet connection anyway. You are required to have them if you want to subscribe to web push, use it as an alternative for BroadCast channel or like a shared worker where safari never decided to implement it. Modify network request, like adding authentication header to a I would be fine by not having dynamically import files never installed, and that it's my responsibility to cache it if i really want to make it work offline. And that i have a understanding that i can't attach event listener afterwards. I want to have more control, I would like to be able to progressively enhance the offline experience by putting things into the cache api when necessary. |
HTML PR whatwg/html#6395 |
@jimmywarting we're not throwing out
That isn't true, not even in my experience as a westerner living in a built-up area. Also, 'connected' isn't a binary in practice. |
At first I was very confused by this thread.
This API would not do what expect at all. It would basically be a different API with the same name. However the proposals threw up a bunch of hard contradictions. Breaking offline support. Introducing compatibility risks. Forcing drastic limits to the scopes which allow Having this run down the clock and not be implemented was comparatively a good outcome. |
Fwiw, worklets also block |
…estonly Automatic update from web-platform-tests No dynamic import in service worker (#27699) Part of whatwg/html#6395 and w3c/ServiceWorker#1356 -- wpt-commits: a02460e35145989c6fe2365f03d1b0006f381d9d wpt-pr: 27699
…estonly Automatic update from web-platform-tests No dynamic import in service worker (#27699) Part of whatwg/html#6395 and w3c/ServiceWorker#1356 -- wpt-commits: a02460e35145989c6fe2365f03d1b0006f381d9d wpt-pr: 27699 UltraBlame original commit: 4f89a146b69c4b14730d2418cbac4447c6ac461a
…estonly Automatic update from web-platform-tests No dynamic import in service worker (#27699) Part of whatwg/html#6395 and w3c/ServiceWorker#1356 -- wpt-commits: a02460e35145989c6fe2365f03d1b0006f381d9d wpt-pr: 27699 UltraBlame original commit: 4f89a146b69c4b14730d2418cbac4447c6ac461a
…estonly Automatic update from web-platform-tests No dynamic import in service worker (#27699) Part of whatwg/html#6395 and w3c/ServiceWorker#1356 -- wpt-commits: a02460e35145989c6fe2365f03d1b0006f381d9d wpt-pr: 27699 UltraBlame original commit: 4f89a146b69c4b14730d2418cbac4447c6ac461a
hmm, stumbled upon an issue today that i'm trying to work my way around, But today i got stuck with "top-level await is not supported in chrome". I know that your goal of all is to make it all work offline no matter what. Even doe I disagree with you on some points... cuz all they is are pain points. You could always implement your own event emitting algoritm on top of the of an another listener to be able to circumvent this lazy registration. const ET = new EventTarget
self.onfetch = () => {
const evt = new Event('fetch')
ET.dispatchEvent(evt)
}
// sometime later
setTimeout(() => {
ET.addEventListener('fetch', () => { ... })
}, 1000) or create some sort of pipeline const middlewares = [ before, handler, after ]
self.onfetch = evt => {
evt.respondWith(async () => {
let response
for await (let pipe of pipeline) {
response = pipe(response)
}
return response
})
}
// sometime later - add a prehook to the middleware array
setTimeout(() => {
middlewares.unshift(fn)
}, 1000) So I don't see any reason to block this functionality of lazy event registration when developers can circumvent this anyway in some way or the other that is more a painful experiences. if i'm importing something in a "sync" manner with top level import statement, and one of the files use top level await to acquire something from IndexedDB that it's dependent upon then i think that should be allowed as well i rather want to acquire something once, then having to do it every time in a fetch event const cacheStorage = await caches.open('v1')
const authKey = await idb.get('key')
/** @type {FileSystemDirectoryHandle} */
const rootHandle = await idb.get('root')
self.onfetch = evt => {...} over this pattern: self.onfetch = evt => {
evt.respondWith(async () => {
const cacheStorage = await caches.open('v1')
...
})
} and there is going to be cases where i wish to conditionally load a polyfill if something isn't supported so again, being forced to import something that isn't needed (b/c it has to be fetched and work offline) with top level import statment is just a waste of bandwidth and time, i rather want to be able to do: var polyfills = []
if (!Array.prototype.groupBy) polyfills.push(import('./array-prototype-groupBy.js'))
if (!Array.prototype.at) polyfills.push(import('./array-prototype-at.js'))
await Promise.all(polyfills) vs import './array-prototype-groupBy.js'
import './array-prototype-at.js' today i'm building something that should be working offline and requires heavy use of service worker + whatwg/fs to function. My point is: I think ServiceWorker are unnecessary heavenly restricted to what a normal Web Worker can do. "By preventing developer shooting them self in the left foot then they shot themself in the right foot instead..." |
doing now all fetch related stuff in sharedWorkers using service worker only to emit events to the functional sharedWorker solved it for me. |
@lemanschik Would it be possible to provide some more details on your use of shared workers in regards to handling the fetches. Namely how you are able to access the shared workers from your service workers. I'm trying to dispatch some fetch events to a wasm module that I can no longer load due to the dynamic import restriction and this seems like it might be a viable work around for me. Thanks. |
@groogiam BroadcastChannels API is my go to solution for that as this is a Shared Message bus that also works cross tabs and windows as long as the domain is the same https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API |
@lemanschik Thanks for the quick reply and clueing me into the BroadcastChannels API. It is working wonderfully. |
Current spec seems to have no description about how to handle dynamic-imported scripts.
We have 3 situations to install scripts:
In 1., a top-level script and all imported scripts are installed because we must guarantee that all scripts for ServiceWorker exist for offline support. Note that importScripts() is allowed to be called only on the initial script evaluation or the install event. See for details #1319 and Intent to Deprecate and Remove: importScripts() of new scripts after service worker installation.
In 2., a top-level script and all imported scripts are installed because of the same reason of 1..
However, in 3., we cannot ensure all imported scripts exist on installation because a service worker could use import() after installation.
My suggestion is we should clarify that a top-level script and static-imported scripts are installed but all dynamic-imported scripts are NOT installed in the spec because dynamic-import is a kind of networking APIs to fetch subresources such as fetch() or XMLHttpRequest that are never installed. If users want to use scripts offline, they should import them with static import. Or, they should explicitly store them in a storage like CacheStorage and dynamically import them as Data URL etc.
The text was updated successfully, but these errors were encountered: