-
Notifications
You must be signed in to change notification settings - Fork 46
Possible to avoid lock-in to async-only implementations #29
Comments
Could this be addressed with the restriction that TLA can not appear in a module that has |
I was wondering that too (see: optional-constraint), but got it in my head that the parent would still block. Some clarification on that would be great! |
My assumption is that without the collision between |
The child module may not have any exports to provide to the parent, but it very well might have side-effects that it expects to complete while the parent waits. Whether or not the child has My stance: in order for TLA to be useful, parents must wait for children. Otherwise, if we settle for restricting TLA to non-exporting modules, and parent modules do not have to wait for children to finish, then TLA will be nothing more than sugar for an immediately-invoked async function expression in the entry point module, which is already possible today. |
If the only way TLA is useful is by introducing a new kind of blocking to the language - effectively Personally, I think there's value in it purely as sugar for an AIIFE. |
I think if hosts want to have extra restrictions, they should be able to do so, e.g. disallowing modules that use top level await. But that's host-specific, and should be out of the scope of the proposal. |
I think variant A (which did not make it to stage 2) may be more in line with the order-is-import-scenario, but I'm not positive.
FWIW I find TLA useful today as just sugar around an async IIFE.
I don't believe the optional-constraint clause restricts to entry scripts. Would TLA champions be ok with something that's just syntax sugar around async-IIFEs? |
I don't think that changing module evaluation order (which blocking would do) is something hosts should be permitted to do. |
No, they would just throw if they see an await. |
If TLA is sugar for an IIAFE, but dynamic Are we imagining that dynamic If TLA ends up being "just sugar around an async IIFE," it seems important to make sure dynamic |
I'd like to add my perspective (since I've been active in the node modules discussions). For ESM/CJS interoperability we will likely be depending heavily on either dual publishing both CJS and ESM, or on runtime ESM-to-CJS translation (e.g. the "esm" package). Users that Currently, this works very well because ESM can be transformed down into CJS without affecting the resulting API very much. But any ESM graph that includes a top-level await cannot be transpiled down into CJS without converting the results into a Promise. From this point of view, top-level await complicates the transition story for Node (especially since top-level await is attractive to users). I think it would be risky to move forward with top-level await until we've had more time for the Node ecosystem to adjust to native modules. Would other options give us the same advantages without the risks? For example, the sugar over AIIFE that's been proposed elsewhere: // An async "statement"
async {
await fileSystem.write(path, 'aksdjfas');
} |
That's a great question. If you're someone who would be happy with TLA-as-sugar-for-AIIFE, then you might be equally happy with a language feature (such as |
Would this be that bad actually? I would expect to get a promise for a module when having TLA in the module or its dependencies. Sure, it could be a bit surprising since nothing in the code explicitly marks up asynchronous dependencies, but I doubt it will be a large problem in practice - you don't make your module asynchronous out of nothing, and without documentation about the implications for using it with CJS. |
@bergus it would be that bad if a transitive dependency, deep in your graph, suddenly forced its entire ancestry to be async unbeknownst to you by adding a TLA. |
@ljharb AFAIK commonjs require cannot and possibly won't be able to load an esm package. Some discussions are happening at nodejs/modules#139 weather require should return a promise of the requested module, but sync loading is not possible. |
@zenorbi yes, I’m quite aware of those discussions, and i don’t agree that sync-looking loading is not possible. Regardless tho, I’m mostly concerned with not adding sleep() to JS, and with making sure that every level of |
@ljharb Sure that's bad, but not really any different than how it currently is - even today any transitive dependency can break your code by suddenly exporting a promise instead of a plain object. The only difference with transpiled ES6 modules would be that the root cause will be harder to find, as all modules implicitly become async and the surprising promise would surface far away from the problem. But that problem is the same with plain ES6 modules (without transpilation), when you have an asynchronous dependency without expecting it and code breaking because of that. The only way to fix that would be to make such imports explicit, e.g. |
@bergus it’s a very different sort of problem when a module ships a breaking change, versus when a module can virally infect every other thing that transitively depends on it. |
It is true that, in general, ESM graphs cannot be transpiled down into sync CJS graphs. But in practice, users aren't using any features that would prevent such transformation. Basically, TLA is going to play havoc with the dual-publishing transition story, where package authors provide both CJS and ESM versions of their modules (either by using Babel or a runtime solution like "esm"). |
"In theory" you could I do say in theory because there's a large amount of stuff that would be need to be done to make things like intrinsics work, cross-agent symbol passing, ensuring the event loop behaves identically outside of |
The current specification preserves the semantics of sync modules with all sync dependencies. That is, if you don't use TLA, and no dependencies do, then there's no lock-in. Does this address the concern? I understand that the rest of the ecosystem could cause you to be using this feature, but that seems like a property of any change to the language. |
Pinging here again to see the status of this concern. |
Given @littledan's answer and lack of engagement further on this issue, I believe that this concern is addressed. If it is not, please re-open this issue. |
At the moment the specification allows implementations to create, instantiate, and evaluate modules in an async or sync fashion. My hunch is that, as is, this proposal will remove the sync option from the table and enforce async all-up. (correct?)
Is there a possible flavor, that the champions of TLA could see, where sync implementations could still be doable? I'm assuming it would mean parent modules don't block for children much like a child module using an async IIFE today.
The text was updated successfully, but these errors were encountered: