tc39 / proposal-dynamic-import

import() proposal for JavaScript

Home Page:https://tc39.github.io/proposal-dynamic-import/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Retry failed dynamic import

Snapstromegon opened this issue · comments

TLDR: Retrying a once failed dynamic import is not possible without hacking around the browser and spec and this leads once failed imports to get blocked for the whole session.

In the current version of the proposal it's declared, that a once failed import should always fail in the future (1.1.2 - 3).
In my understanding this came from the fact, that a once resolved import should always return the same module instance so two calls to the same import don't retrigger the module setup.

To this point I'm completely in favour of the proposal since it matches as closely as possible the static import.

But if we now change from successful imports to a failed import I can't see the need to not retry an earlier failed import. A typical usecase would be to have a single page app which loads custom elements as modules as they are needed. If a user now has a short network outage he will never during his session be able to reimport that module (a hacky workaround is to add a nonce to the url, but that also breaks the "each module is only instantiated once" rule since now one module becomes two).

In my opinion a call to import should work the following way:

Untitled Diagram

That way it's possible to retry a failed import and you don't need to hack around the browser and specification like described above. This would not touch static import in any way and there's already the misconception out there that dynamic import is retryable.

If there is any reason except for ease of implementation for the browsers to keep the current behavior for dynamic import, please let me know.

I've added an item to the upcoming TC39 agenda to discuss loosening this restriction. In particular I think it should match the requirements of HostResolveImportedModule so that failures are not required to be cached.

If we get TC39 signoff for allowing hosts (like web browsers) to not cache the results, the bulk of the work here would be done in the HTML Standard repository, which specifies exactly how URLs are fetched and the results are cached. @hiroshige-g and I are exploring this space a bit. Tentatively we have the following thoughts:

  • This should behave the same for static and dynamic imports. For example we should just not cache failed fetches (we currently do). This is important if, for example, you do import("./a") where ./a statically imports ./b and ./b has a transient network error. You will want to just retry import("./a").
  • We are unsure how to treat parse errors. Should we continue caching them? Or should we allow retrying failed parses?
  • How this impacts multiple parallel module graph fetches seems complicated.

Thanks for your answer and I like your idea.
My thoughts on your bulletpoints are the following:

  • I strongly agree
  • I think the ideal way would be to reject the promise with a parsing error. That way a developer can decide himself if he wants to retry or give up. I understand the concern by thinking "the file isn't going to change anyway", but I can see some cases where the problem might be fixed before the website reloads.
  • I think the start of a module fetch needs to be synchronized between two module graph fetches. That way multiple parallel subgraphs might be able to share the same subgraph for module loading. Isn't this also a problem with multiple module graphs fetching the same modules at slightly different times? I think this is a problem that doesn't only apply to this problem.

Personally I think it would be better to only allow retry of modules that failed due to errors during the link stage (but not parsing errors). This would cover cases where if a module failed to fetch but wouldn't allow retry-ing a module that simply throws an error during evaluation (e.g. wrong environment).

@Jamesernator I am confused by your statement. Evaluation errors are not parsing errors.

From the diagram it seems to imply that given this:

// file1.js
console.log("Hello!");

throw new Error("Failed!");
// file2.js
import("./file1.js")
  .catch(err => import("./file2.js"));

Would print Hello! twice, but I think it would be better that only linking errors (e.g. failed to even get source text) should be allowed to be retried expect for parsing errors which while in the linking phase are program errors rather than loading errors.

Even though I didn't mean to have this as a possibility during the creation of the graph, I see some usecases where it would be good to retry any failed module which didn't yet resolve successfully.

On the other hand the sideeffects would be really unexpected as a developer and I always see cases where a module might break in weired ways because of a retry after execution start.

Overall I agree that your example should indeed only log Hello! once and once execution of a module started there should'nt be a way back.

throw new Error("Failed!") is not a parsing error. It is an evaluation error.

Closing this since Domenic's issue was closed and this is no a duplicate.

Might be interesting to force the retry on failed by the spec, but in the means of this issue the current solution is fine.

Sorry to add to a close issue... Just double checking.
I fit precisely the use case described by @Snapstromegon -- I have a SPA; what I am actually doing, is loading the bare minimum of my SPA and then slowly, in the background, load the rest of the app using import(). I made sure that if an import failed, the code would retry after 5 seconds assuming a network error. I was confused when I noticed that retrying just wasn't happening...

Am I right in saying that this behaviour has now been rectified, and that future browsers will indeed re-attempt loading failed imports? And, do you guys know which version of Chrome/Safari is taking on the correct behaviour? Is there a ticket anywhere?

I am trying to decide if I should scrap dynamic imports altogether for my SPA, since this limitation basically renders my code useless -- and dynamic imports for SPAs kind of unworkable...