Do loaders need a way to initiate module resolution other than "define?"

135 views
Skip to first unread message

Ed Swift

unread,
Jul 13, 2014, 3:04:52 AM7/13/14
to amd-im...@googlegroups.com
Continuing a conversation from the common config thread:


The AMD loader I'm working on does not have a special way to kick off module resolution. The entire API is a global `define` function. When `define` is called, any dependencies are immediately loaded (if they haven't already started loading). When all dependencies in the chain are finished loading, the factory executes.

This has been good enough for my purposes so far, but is there some caveat I'm missing? What are some reasons for having a separate function to initiate module resolution, rather than simply resolving dependencies as soon as they're encountered and running factories as soon as all dependencies are met?

Ben Hockey

unread,
Jul 14, 2014, 11:15:54 AM7/14/14
to amd-im...@googlegroups.com
an example that's fairly easy to reason about is that after building your source files, you end up with multiple modules concatenated into one file.  when that file is loaded, the define call for all those modules is executed but if you don't defer execution of those factories until the modules have been required then your code will be executed differently after a build than before a build.  you can think of define as a way to register a module but some other module (or a call to require) is what causes that module's factory to be executed.

another issue you face if you don't have a way to kick off module resolution is you no longer have a way to defer the loading of modules.  one common technique is to defer loading a portion of an application until it's clear that the user intends to interact with that part of the application (e.g. a user account settings page).  the typical way of doing this is to use a call to require in response to a user's action.  this can avoid needing to load (and execute) this code during initialization of your app and improve performance.

ben...

Ed Swift

unread,
Jul 14, 2014, 12:34:53 PM7/14/14
to amd-im...@googlegroups.com
an example that's fairly easy to reason about is that after building your source files, you end up with multiple modules concatenated into one file.  when that file is loaded, the define call for all those modules is executed but if you don't defer execution of those factories until the modules have been required then your code will be executed differently after a build than before a build.  you can think of define as a way to register a module but some other module (or a call to require) is what causes that module's factory to be executed.

I do defer execution of the factory until any dependencies of the module in question have been filled, so things executing in a different order should be a non-issue. The only things that will execute in an undefined order will be things that don't depend on each other anyway. Assuming the script doing the concatenating is smart enough to give anonymous modules ids matching the paths where they were found and so on, of course.
 
another issue you face if you don't have a way to kick off module resolution is you no longer have a way to defer the loading of modules.  one common technique is to defer loading a portion of an application until it's clear that the user intends to interact with that part of the application (e.g. a user account settings page).  the typical way of doing this is to use a call to require in response to a user's action.  this can avoid needing to load (and execute) this code during initialization of your app and improve performance.

 Maybe this is the real issue. I guess you could describe what I'm doing as eager loading:

- "Hey, a module has been defined. Let's load its dependencies."
- "Hey, this module has all its dependencies filled, lets execute its factory."

And the `require` way of doing things is more like lazy loading:

- "Hey, a module has been defined. Let's stick it somewhere in case some other module needs it."
- "Hey, a call to `require` happened! Let's start loading these dependencies."

Lazy loading may be the way to go, but adding another function to the global scope that is invoked in pretty much exactly the same way as `define` bugs me. At this point I'm thinking about having the `define` function return a Module object (the same type of object the inbuilt "module" dependency resolves to), and giving modules a `resolve` function or similar. So instead of this:

    require(["foo"], function (foo) {
        // do stuff
    });

You would have this:

    define(["foo"], function (foo) {
        // do stuff
    }).resolve();

It's ugly, but it makes sense (to me, anyway) and keeps the global scope clean.

I wonder if there should be some mention of lazy vs. eager loading in the spec, even if only to say the behavior is unspecified? 
Reply all
Reply to author
Forward
0 new messages