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

Re-throw evaluation errors on deferred namespace property access #43

Closed nicolo-ribaudo closed 3 months ago

nicolo-ribaudo commented 4 months ago

This commit changes the behavior of namespaces obtained through import defer to re-throw evaluation errors even if the module is fully evaluated:

import defer * as ns from "module-that-throws";
try { ns.foo } catch { console.log("Error!") }
try { ns.foo } catch { console.log("Error!") }

The code above is now guaranteed to print "Error!" twice, and it doesn't depend on whether other modules already evaluated "module-that-throws".

This is different from the existing behavior of namespace objects, so we have to use a different namespace object for deferred import:

import defer * as ns1 from "module-that-throws";
import * as ns2 from "module-that-throws";

Promise.resolve().then(() => {
  try { ns1.foo } catch { console.log("Error1!") }
  try { ns2.foo } catch { console.log("Error2!") }
});

Assuming that this module's body is evaluated due to a cycle (module-that-throws imports it, and module-that-throws is the entry point), the code above will print "Error1!" and not "Error2!". This means that ns1 !== ns2. Namespace objects are still cached, so if you import defer the same module twice you will get the same namespace object.

Thanks to this change, we can now also make "trying to evaluate a deferred module in a cycle" an error, rather than silently skipping its evaluation as done in #39. This ensures that a binding cannot be accessed during evaluation to then disappear as soon as the module is done evaluating with an error.

Fixes #41 cc @guybedford

nicolo-ribaudo commented 3 months ago

@guybedford I updated the spec. It turns out the TODO I left is already handled, but I left it as a NOTE since it's not trivial to notice.

One possible optimization - in the case where a deferred module has already successfully executed, we could provide the normal namespace instead of the deferred namespace without there being any observable difference. This could possibly be a spec note or even specified.

Guy and I discussed this in the modules call last week -- this cannot be done because the identity of the two namespaces is observably different.