Skip to content

Commit 895430a

Browse files
committed
dynamic imports
1 parent a94ba92 commit 895430a

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

chapters/ch08.asciidoc

+69
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,75 @@ counter.increment()
479479
console.log(counter.default) // <- 2
480480
----
481481

482+
==== 8.2.4 Dynamic +import()+ Statements
483+
484+
At the time of this writing, a proposal for dynamic +import()+footnote:[You can find the proposal specification draft here: https://mjavascript.com/out/dynamic-import.] expressions is sitting at stage 3 of the TC39 proposal review process. Unlike +import+ statements, which are statically analyzed and linked, +import()+ loads modules at runtime, returning a promise for the module namespace object after fetching, parsing, and executing the requested module and all of its dependencies.
485+
486+
The module specifier can be any string, like with +import+ statements. Keep in mind +import+ statements only allow statically defined plain string literals as module specifiers. In contrast, we're able to use template literals or any valid JavaScript expression to produce the module specifier string for +import()+ function calls.
487+
488+
Imagine you're looking to internationalize an application based on the language provided by user agents. You might statically import a +localizationService+, and then dynamically import the localized data for a given language using +import()+ and a module specifier built using a template literal which interpolates +navigator.language+, as shown in the following example.
489+
490+
[source,javascript]
491+
----
492+
import localizationService from './localizationService'
493+
import(`./localizations/${ navigator.language }.json`)
494+
.then(module => localizationService.use(module))
495+
----
496+
497+
Just like with +import+ statements, the mechanism for retrieving the module is unspecified and left up to the host environment.
498+
499+
The proposal does specify that once the module is resolved, the promise should fulfill with its namespace object. It also specifies that whenever an error results in the module failing to load, the promise should be rejected.
500+
501+
This allows for loading non-critical modules asynchronously, without blocking page load, and being able to gracefully handle failure scenarios when such module fails to load, as demonstrated next.
502+
503+
[source,javascript]
504+
----
505+
import(`./vendor/jquery.js`)
506+
.then($ => {
507+
// use jquery
508+
})
509+
.catch(() => {
510+
// failed to load jquery
511+
})
512+
----
513+
514+
We could load multiple modules asynchronously using +Promise.all+. The following example imports three modules and then leverages destructuring to reference them directly in the +.then+ clause.
515+
516+
[source,javascript]
517+
----
518+
const specifiers = [
519+
`./vendor/jquery.js`,
520+
`./vendor/backbone.js`,
521+
`./lib/util.js`
522+
]
523+
Promise
524+
.all(specifiers
525+
.map(specifier => import(specifier))
526+
)
527+
.then(([$, backbone, util]) => {
528+
// use modules
529+
})
530+
----
531+
532+
In a similar fashion, you could load modules using synchonous loops or even +async+/+await+, as demonstrated next.
533+
534+
[source,javascript]
535+
----
536+
async function load () {
537+
const { map } = await import(`./vendor/jquery.js`)
538+
const $ = await import(`./vendor/jquery.js`)
539+
const response = await fetch(`/cats`)
540+
const cats = await response.json()
541+
$(`<div>`)
542+
.addClass(`container cats`)
543+
.html(map(cats, cat => cat.htmlSnippet))
544+
.appendTo(document.body)
545+
}
546+
load()
547+
----
548+
549+
Using +await import+ makes dynamic module loading look and feel like static +import+ statements. We need to watch out and remind ourselves that the modules are asynchronously loaded one by one, though.
550+
482551
=== 8.3 Practical Considerations for ES Modules
483552

484553
When using a module system, any module system, we gain the ability of explicitly publishing an API while keeping everything that doesn't need to be public in the local scope. Perfect information hiding like this is a sought out feature that was previously hard to reproduce: you'd have to rely on deep knowledge of JavaScript scoping rules, or blindly follow a pattern inside which you could hide information, as shown next. In this case, we create a +random+ module with a locally scoped +calc+ function, which computes a random number in the +[0, n)+ range; and a public API with the +range+ method, which computes a random number in the +[min, max]+ range.

0 commit comments

Comments
 (0)