Learning from requirejs

# John J Barton (13 years ago)

Dave Herman has a blog post Synchronous module loading in ES6 calculist.org/blog/2012/03/29/synchronous-module-loading-in-es6

Which says:

In ES6, we’ll define a restricted version of the syntax to be used in synchronous settings, which makes it illegal to do synchronous loads. Within a blocking script, the only access to modules is via the dynamic loading API: <script>

System.load("jquery.js", function($) { $('myelement').style({ 'background-color': 'yellow' }) }); </script>

This is operationally similar to how require.js works: when the script tag is processed the request for 'jquery.js' and the callback is registered to be run asynchronously with the document parsing. Like require.js, this async processing needs to pull down a dependency tree over the network before running the callback.

And like require.js we will have difficulty resynchronizing the script loading with document loading. For example, the above code may fail if the callback fires before 'myelement' is parsed.

jjb

# Brendan Eich (13 years ago)

John J Barton wrote:

And like require.js we will have difficulty resynchronizing the script loading with document loading. For example, the above code may fail if the callback fires before 'myelement' is parsed.

That's a bug on the HTML author's part. The 'myelement' declaring markup must have come before that script, or the whole thing should have used DOMContentReady (but see stackoverflow.com/questions/2024018/using-domcontentready-considered-anti-pattern-by-google).

Assuming a bug (failure to order dependent after dependency) to justify something -- what, exactly? -- doesn't make a convincing argument for anything except fixing the bug.

# David Herman (13 years ago)

On Mar 29, 2012, at 4:47 PM, John J Barton wrote:

And like require.js we will have difficulty resynchronizing the script loading with document loading. For example, the above code may fail if the callback fires before 'myelement' is parsed.

I'm not sure I understand. Why doesn't something like this work?

<script>
System.load("jquery.js", function($) {
    $.ready(function() {
        $('myelement').style({ 'background-color': 'yellow' })
    });
})
</script>
# John J Barton (13 years ago)

On Thu, Mar 29, 2012 at 4:55 PM, David Herman <dherman at mozilla.com> wrote:

On Mar 29, 2012, at 4:47 PM, John J Barton wrote:

And like require.js we will have difficulty resynchronizing the script loading with document loading. For example, the above code may fail if the callback fires before 'myelement' is parsed.

I'm not sure I understand. Why doesn't something like this work?

<script>    System.load("jquery.js", function($) {        $.ready(function() {            $('myelement').style({ 'background-color': 'yellow' })        });    })    </script>

To prevent a possible performance problem caused by synchronous module loading that blocks document loading, you are proposing to enforce asynchronous module loading. This sets up developers for a non-deterministic failure mode. Not everyone uses jquery and some that do may easily forget about the race. Can we do better?

For example if we had system.load() wait to fire the callback until DOMContentLoaded and offered system.loadImmediate(), developers would be altered to this issue simply by having such an API.

The example callback has two dependencies, jQuery and 'myelement'. Only one of these has expressed to the load() operation. Perhaps there are other ways of expressing the second dependency.

jjb

# Wes Garland (13 years ago)

We face basically this same problem in our day-to-day development -- here's how we do it: food for thought

We use BravoJS as our module system, which targets CommonJS Modules/2.0-draft 8 (not an officially endorsed standard). From the ten-mile-POV, BravoJS works by loading the entire module dependency tree asynchronously (via script-tag injection) when the main module is declared. As dependent modules are loaded, we examine their dependencies and load them, too. Once all dependencies have been loaded, we initialize the main module (run it). Modules in CommonJS are initialized (run) when they are first require()d. Note that this is different from RequireJS, which initializes modules eagerly.

We have the ability to lazy-load modules, but find that we seldom need this. We just don't have any large enough "leaf" modules to need this

We use jQuery in our modules freely: we do this by including jQuery old-school, in the document head. I guess this is the 'synchronous load' issue you were discussing? We could address it in our own model by making jQuery a dependency of the main module (and refactoring jQuery to work in a CommonJS loader).

We know that jQuery is always available, because our "main module" is always declared in the last script tag in the document; generally between /body and /html.

Note that this means that we can't execute CommonJS code while the document is loading. This has not been a problem for us, although if we wanted to allow this, we could without a lot of effort. We do freely use CommonJS code in our HTML, though -- something like this --

<form onsubmit="require('validate').form(this);" action="return module.main.formAction(this);">

..that pattern has been a very useful: flexible, easy to use, plays-well-with-older-code.