tc39 / proposal-defer-import-eval

A proposal for introducing a way to defer evaluate of a module
https://tc39.es/proposal-defer-import-eval
MIT License
208 stars 12 forks source link

[Question] Is this the solution for below case? #23

Closed loynoir closed 10 months ago

loynoir commented 1 year ago

Is this the solution for below case?

Is so, I think, should also add to example use case, as I think it's better to read at first glance.

import foobarDep from 'foobar-dep'
import browserDep from 'browser-dep' with { lazyInit: true }
import denoDep from 'deno-dep' with { lazyInit: true }
import nodeDep from 'node-dep' with { lazyInit: true }

function foobar() {
  if (IS_BROWSER) {
    return [foobarDep(), browserDep()]
  }

  if (IS_DENO) {
    return [foobarDep(), denoDep()]
  }

  if (IS_NODE) {
    return [foobarDep(), nodeDep()]
  }

  throw new Error()
}

export { foobar }
nicolo-ribaudo commented 1 year ago

That case would still load all the browser-dep, deno-dep, and node-dep modules with all their dependencies, even if only one will be executed.

If you can access the IS_* conditions in the top-level scope, I would recommend doing this instead:

import foobarDep from 'foobar-dep'
import browserDep from 'browser-dep' with { lazyInit: true }
import denoDep from 'deno-dep' with { lazyInit: true }
import nodeDep from 'node-dep' with { lazyInit: true }

const { default: dep } = await import(
  IS_BROWSER ? 'browser-dep' : IS_DENO  ? 'deno-dep' : IS_NODE ? 'node-dep' : error()
);

function foobar() {
  return [foobarDep(), dep()]
}

An alternative is to just do this:

import foobarDep from 'foobar-dep'
import dep from 'dep'

function foobar() {
  return [foobarDep(), dep()]
}

And use three different import maps in the three environments to map dep to the correct module.

loynoir commented 1 year ago

Avoiding unnecessary execution is a well-known optimization in the Node.js CommonJS module system, where there is a smaller gap between load contention and execution contention

@nicolo-ribaudo

Ah, above code is just a simple reproduce for optional dependency.

import dep from 'https://slow.network/optional-dep' with { lazyInit: true }

Current ESM lack of an SIMPLE AND AST-FRIENDLY way to write optional dependency.

I think, SIMPLE AND AST-FRIENDLY optional dependency should also be part of unnecessary web execution optimization consideration.

nicolo-ribaudo commented 10 months ago

@loynoir You can currently use top-level await to implement optional dependencies:

try {
  var dep = await import("https://slow.network/optional-dep");
} catch {}

if (dep) {
  // dep has been loaded
} else {
  // loading dep failed
}

We are also planning a dynamic import form of this proposal, similar to https://github.com/tc39/proposal-source-phase-imports:

try {
  var dep = await import.defer("https://slow.network/optional-dep");
} catch {}

I agree in general that we should consider improvements to ECMAScript's module system, but we are trying to keep proposals focused on a single problem/solution.