Skip to content
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

Retrying failed to fetch dynamically imported modules #116

Open
blittle opened this issue Mar 20, 2019 · 11 comments
Open

Retrying failed to fetch dynamically imported modules #116

blittle opened this issue Mar 20, 2019 · 11 comments

Comments

@blittle
Copy link

blittle commented Mar 20, 2019

I'm trying to understand how to resolve the following scenario:

  1. The user is online and loads my web app.
  2. Later, the user goes offline and navigates somewhere in the app which triggers a dynamically imported module resolved via an import map.
  3. Because I'm offline, the module loading will result in an error of Failed to fetch dynamically imported module. The app notifies the user that they are offline.
  4. Now the user reconnects and becomes online.
  5. We retry the original module fetch, which fails because the browser never retries the original request.

If I wasn't using import maps, I could force a retry by adding a query parameter to the URL I'm importing. With module maps, is there any way to retry a module import that has previously failed?

Note: in my scenario above, I could solve the problem by building a wrapper around the native import() that keeps track of whether the user is online/offline and prevent a module request from going out unless they are online. But that doesn't resolve all of the scenarios where a module resolution might fail, and the app may want to retry.

@domenic
Copy link
Collaborator

domenic commented Mar 20, 2019

With module maps, is there any way to retry a module import that has previously failed?

Sure. You could add a query parameter to the URL you're importing.

@blittle
Copy link
Author

blittle commented Mar 20, 2019

@domenic that's easy when not using import maps. But how do I do that when using import maps? As far as I can tell, import('lodash?retry=1') isn't supported.

@blittle
Copy link
Author

blittle commented Mar 20, 2019

Also, because import maps can't be dynamically changed post resolution, I also can't dynamically change the import map to point to a new URL with a query parameter.

@guybedford
Copy link
Collaborator

This use case relies on at the very least a registry "delete" API. Any type of JS registry reflection spec is still a long way off unfortunately.

@blittle
Copy link
Author

blittle commented Mar 29, 2019

@guybedford it seems as though, when using native modules defined in an import map, the best option is to keep track of navigator.onLine and only request the module when the browser thinks it is connected to the internet. Not an ideal solution, because navigator.onLine throws many false positives.

@guybedford
Copy link
Collaborator

@blittle can you not manage this at the service worker level? That probably seems preferable for these use cases.

@blittle
Copy link
Author

blittle commented May 13, 2019

@guybedford generally I would agree, but I think there is still an edge case to handle due to:

However, service workers are not available on first load. Thus, they can't really be a part of the critical infrastructure used to load modules. They can only be used as a progressive enhancement on top of fetches that will otherwise generally work.

Imagine a scenario where the app is loaded for the first time, and no service worker is available. Then for some reason the browser gets intermittent connectivity, and there are subsequent modules defined in the module map that are now failing to load, and no service worker is available intercept.

@guybedford
Copy link
Collaborator

guybedford commented May 17, 2019

@blittle as mentioned I think the best solution is the dependency reflection and registry delete API combination in such a case:

import('app').catch(async err => {
  if (!isNetworkError(err)) throw err;
  unloadModuleAndDependencies(import.meta.resolve('app'));
  return import('app');
});t

function unloadModuleAndDependencies (moduleId) {
  const deps = ModuleReflect.getDependencies(moduleId);
  if (deps) {
    ModuleReflect.delete(moduleId);
    deps.forEach(unloadModuleAndDependencies);
  }
}

The above depends on the hypothetical specifications for ModuleReflect.delete , import.meta.resolve and ModuleReflect.getDependencies which as I say are all quite far off into the future still.

I didn't realise Service Worker still hasn't solved the first-load attachment case... but I wouldn't bet on the above arriving any sooner :P

@guybedford
Copy link
Collaborator

(and in case it isn't clear - ModuleReflect is a really really bad strawman used just for discussion here, and is by no means anything being discussed or proposed, and likely shouldn't be).

@blittle
Copy link
Author

blittle commented May 17, 2019 via email

@domenic domenic added this to the Full featured milestone Jul 3, 2019
@domenic domenic modified the milestones: Full featured, API Sep 23, 2019
@AlonMiz
Copy link

AlonMiz commented Jan 15, 2023

I am joining this train a bit late, but it's still an issue.
I would love to be able to delete the asset/map to the failed asset. in the first place, I think the failed response shouldn't be cached.
We came up with a hacky workaround. it partially solves the problem.
Anyhow, you are welcome to try this, to be able to "retry" dynamic module failure
The solution is "partially" solved as the rest of the tree will not be retried. We cannot programmatically add a query param to them.
I wrote a quick article about that: https://medium.com/@alonmiz1234/retry-dynamic-imports-with-react-lazy-c7755a7d557a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants