Skip to content

Proposal: Async imports #3100

Description

@tinganho

Most module bundlers bundles everything into one file e.g. browserify, requirejs etc. But if you are developing a very large scale application you might not find it suitable to have everything bundled into a single file. What you might want is to have it bundled into multiple files, i.e. sub graphs of the main dependency graph. So if you visit a page, you only want to download the contents of that page instead of contents of others. This is a very good use case for SPA:s, because in today's day and age they need to be bundled into a single file, because they need to handle navigations and actions through all pages.

But in order for it to work we need async imports. To just give you an overview of what impact this feature could have — let's have a look at a general defined router for an SPA:

class Router extends SomeRouter {
  ...
  private routes = {
     '/users/:id': 'renderUserPage'
  }
  ...
  renderUserPage() {
     async import {
       UserPageModel from './pages/user/UserPageModel';
       UserPageView from './pages/user/UserPageView';
     } {
       let model = new UserPageModel({ id: 1 });
       let view = UserPageView({ model: model });
       view.render();
     }
  }
  ...
}

Now, the above async import statement could tell a module loader to bundle its' sub-dependency graph. So all files that UserPageModel and UserPageView requires could be bundled into a single file. Now, If you want to navigate to a different page you don't want to download the already downloaded modules. So a smart module bundler will bundles different subtracted modules of a bundle. So if a user navigates all pages in an application he would have download the whole dependency graph without over fetching something.

This proposal was inspired by how Facebook does its' closed source module handling. I know that they do something similar. And it also very similar to webpack's code splitting. It would be good if TypeScript support this with all the tooling support.

Activity

  1. kitsonk commented on May 10, 2015

    @kitsonk
    Contributor

    RequireJS does not bundle everything into a single file. By its nature, it was built to support asynchronous module loading (a.k.a. AMD). It is R.js (an affiliated project) that allows you to combine many modules into a single file. In AMD, the concept of layers, which would be strategically be built to make sure that only the dependencies are loaded are loaded. Asynchronous module loading makes lots of logic client side, but has a potentially limited value server side. TypeScript targets both.

    I don't really think this is the domain of TypeScript. TypeScript supports AMD outputting and CommonJS modules and soon what is referred to as UMD and will support ES6 modules. That is the "job" of TypeScript in my opinion and further build optimisation is left to much more focused tooling like UglifyJS, Closure, R.js, etc. etc. So, I don't think it is appropriate for TypeScript, personally.

  2. tinganho commented on May 10, 2015

    @tinganho
    ContributorAuthor

    Loading is not equal to bundling. AFAIK when you run its bundle tool requirejs bundles everything into one file[1]? Though you can specify multiple bundles, though they don't create the sub dependency graph described above.

    You could create the loader described above with AMD and CommonJS(in fact I think webpack does it). You would lose all the tooling support in TS(unless you want to write declaration files). So preferably it should be a built-in feature of TS.

    [1] RequireJS Optimization

  3. kitsonk commented on May 11, 2015

    @kitsonk
    Contributor

    AFAIK when you run its bundle tool requirejs bundles everything into one file[1]?

    Actually, it can build multiple layers and then loads it asynchronously.

    You could create the loader described above with AMD and CommonJS

    So are you suggesting that TypeScript get into the module loading business as well? The packaging came in via #2605. In ES6, module loading is going to be handle natively by the host and allows for async loading. I am not sure why TypeScript would want to solve that problem at a semantic language level.

    Part of your original problem:

    What you might want is to have it bundled into multiple files, i.e. sub graphs of the main dependency graph.

    Is exactly what RequireJS does today into building into layers, with dependency management. Other tools do the same thing. Again, better to let them solve that part of the issue, in my opinion, versus building it in semantically into TypeScript. Often times those layers can be very environment specific and I wouldn't want some upstream coder deciding that I needed to load an individual module asnyc. I want to do that whole thing as part of my build chain tooling. I might very well output a set of layers for IE9 and a different set for Chrome, because of what is support/performance/etc. of my code.

  4. tinganho commented on May 12, 2015

    @tinganho
    ContributorAuthor

    Is exactly what RequireJS does today into building into layers, with dependency management.

    The build layers in RJS are not the same as the sub dependency graph I mentioned. Even though they represent a "sub dependency graph". You could for instance fetch an unused module in the up-level layers before you fetch one at the bottom. With the proposed solution no over fetching will occur.

    If you want to create a sub dependency graph bundle you would need require.ensure in CJS and require() in AMD. And TS doesn't compile into these with their import statements today. However, you could call those functions directly. But then you would lose the tooling support(unless you want to do some tiresome work of defining declaration files and rewire your build for every async import call).

  5. mhegazy commented on May 28, 2015

    @mhegazy
    Contributor

    This is outside the scope of TypeScript at the current time.

  6. electricessence commented on May 20, 2016

    @electricessence

    ++

  7. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Out of ScopeThis idea sits outside of the TypeScript language design constraintsSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions