2 ms·
Because the ESM standard specifies that modules are loaded by URL and themselves might initialize async. Once anything is async the whole call stack into it is.
by eyelidlessness 5y ago
Because the ESM standard specifies that modules are loaded by URL and themselves might initialize async. Once anything is async the whole call stack into it is. See also various discussions of “colored functions” which have made the rounds again recently.
- wruza 5y agoWhy doesn't nodejs just make modules async? // main.js const fs = require("fs") const foo = await require_async("foo") await foo.sleep(1000) // other.js require("./main.js") // RequireError: main.js returned an unsettled promise, use require_async() Wouldn't that be better of both worlds?
- eyelidlessness 5y agoNode did make modules async (just ESM modules). They can’t/won’t make CJS async because their synchronous semantics are a guarantee they determined not to break well before ESM. If you want async modules, ESM is the solution for that.
- wruza 5y agoESM will be the solution to that, when all the major toolchains catch up. I wouldn't sell this future short, but we aren't there yet either.
- eyelidlessness 5y agoI agree but I think we’re there (if kicking a bit and screaming a bit).
- wtfrmyinitials 5y agoThis is inaccurate, or at least misleading. Your code doesn’t begin executing until all of the modules are loaded so it doesn’t “make the call stack async” just because of the import system. Module loading that’s async from the point of view of user code is also supported, but it’s use is rare.
- eyelidlessness 5y ago> This is inaccurate, or at least misleading. No it’s not. > Your code doesn’t begin executing until all of the modules are loaded. This is inaccurate, or at least misleading. This, or some aspect of it, is both possible and relatively common: import foo from 'foo'; await foo.bar(); await Promise.all([ import('other'), import('stuff'), ]); // Admittedly this is less common but also valid! import yet from 'more-stuff'; The asynchrony of ESM was controversial before it even became a standard. But it’s necessary because it allows network I/O. And most of the above patterns being relatively common is one of the major use cases for bundling, because it also introduces an indefinite waterfall. In terms of rarity, dynamic import calls are already quite common for “lazy”/“suspense” or their equivalents in quite a lot of real world code, and likely to become moreso with React Server Components and other similar solutions deferring to server rendering. Yes, an import statement is [semantically] blocking. But even so it’s important to know that it’s performing async I/O.
- wtfrmyinitials 5y ago> import foo from 'foo'; > await foo.bar(); You only have to `await foo.bar()` if foo.bar was an async function already, the module system is irrelevant. You would still need to await it even if it was `require()`'d in. import baz from 'baz'; baz.foo(); Works just fine, no `await` necessary. > await Promise.all([ > import('other'), > import('stuff'), > ]); Of course these have to be awaited, at this point you're explicitly trying to load new code while the program is running. It's no different than any other kind of async IO. > import yet from 'more-stuff'; I assume this is included to imply that loading `yet` is blocked by the `await Promise.all` above it to show that import statements can run after module resolution. If this was your intent you are mistaken, that import statement is still resolved before execution begins. // main.js console.log('a') await new Promise(res => setTimeout(res, 1000)) console.log('b') import { foo } from "./foo.js"; // foo.js export function foo() { } console.log('c') Results in c a [one second pause] b > Yes, an import statement is [semantically] blocking. But even so it’s important to know that it’s performing async I/O. No, it really isn't. From the POV of the application code it's irrelevant. This is trivially proven by the existence of module bundlers which convert non-dynamic `import` statements into one big file that doesn't do anything asynchronous to initialize. (edit: fighting with formatting)