4 ms·
I find using ES Modules in Node.js to be full of gotchas and anti-patterns - sticking with CommonJS seems more functional in most cases. 1: With ES Modules, im
by FunnyLookinHat 6y ago
I find using ES Modules in Node.js to be full of gotchas and anti-patterns - sticking with CommonJS seems more functional in most cases.
1: With ES Modules, imports are asynchronous.
Synchronous imports are incredibly powerful. You can ensure that your application doesn't block on first requests, but instead on load. Initializing connection pools, setting up caches, etc.
You'll also find quite a few packages in NPM that explicitly depend on require over import just so that they can perform the bindings they have to before things get going (see https://www.npmjs.com/package/honeycomb-beeline https://www.npmjs.com/package/honeycomb-beeline as an example).
2: With ES Modules imports are commonly destructured.
I know it makes your code feel lighter to have const { add } = import("whatever"); - but as your files grow larger, and those imports start to become complicated bits for middleware or other features, you're just making it more difficult for future maintainers to figure out what those functions mean. A bit of context never hurt anybody, and for my money I'll be that this:
app.use(check);
is not nearly as useful as
app.use(Validator.check);
The example may be contrived - but 9/10 times I find this makes code better than the alternative.
All of these arguments go out the door for Frontend work - that's a totally different terrain. I think one of the big arguments is that developers want to use the exact same JavaScript on frontend and Node applications - but that's an argument unwilling to admit the fact that they're two very different tasks. :)
- richthegeek 6y agoA nice middleground on your example is renaming during destructure: `import { check as ValidatorCheck } from "validator";` This keeps the context at the expense of a slightly more verbose import. Although the primary reason I use destructuring is to enable tree-shaking, and that's only really applicable for the frontend. I'm not sure why I'd destructure on the backend.
- spankalee 6y agoModules load and eval asynchronously, because there's no viable way for network connected device like a browser to do it synchronously. But modules still run in depth-first import order, so there is a perfectly fine way do work before other modules run by putting that init code in a separate module and importing it first. Your second point applies the same to imports and require.
- jsmith45 6y agoImports are asynchronous, but they are still eager, and still run before the code in the module. Furthermore, the bodies of modules will still execute in the order you import them in. Basically all being Asynchronous means is that the interpreter is allowed to use multiple threads to load and parse the referenced modules "out of order", but will still execute them in order. Thus your whole static import tree will be resolved before the first request comes in, assuming you start the server listening in the body of your main module. If you start it in the body of an imported module, and have other later imports that have start-up task side effects, then yeah you might have some requests come in before those later side effects happen, but why would you structure your code like that? The modules approach generally favors not having side effects in from importing modules, except the program's top level module of course. Now dynamic imports are fully asynchronous, returning a promise. But even then you can structure your code to ensure they are loaded before your start accepting connections, or you can structure dynamic imports to be lazy and on-demand. This is not much different from CJS where you can also structure any dynamic imports to be either lazy or eager. The main difference is that with modules you need to deal with a promise while with CJS's require you don't.
- FunnyLookinHat 6y agoI guess the only thing you would gain then is the ability to have circular dependencies - or some other pattern nested in your application. Right? That was super helpful - thanks for explaining! I'm still lost as to what the advantage is other than a faster start-up time. What am I missing?