public
Created

(Un)CommonJS Asynchronous Module Proposal Differences

  • Download Gist
module-proposal-diffs.md
Markdown

In both cases below, ID is the exports object of the "id" module.

CommonJS/Modules/Async/A

require.ensure(["id"], function (require) {
    var ID = require("id");
})
  • In this example, the "id" module does not need to be loaded before executing this module. Because it uses the require("id") syntax, it is not simple to distinguish it from a dependency that must be preloaded.

UncommonJS/Modules

require.async("id", function (ID) {
}, function (error) {
});
  • does not accept multiple identifiers. The presumption is that it will be more common and usable for the loaded module to statically depend on anything you would be tempted to import in the same async call. If this presumption does not pan out (in my own use, it has), it is easy enough to extend this specification with spread/rest (variadic) arguments.

What are your thoughts on just passing the module exports of 'id' to the callback instead of require? I find having to duplicate the ID in two places to be awkward, and this sort of call should only have a small set of IDs passed to it.

I’ve made a correction. It turns out that I did not involve UncommonJS/Promises in the current UncommonJS/Modules specification. It takes optional callback and an errbacks. /cc @tobie @jrburke

@jrburke to clarify, the purpose of this gist is to compare Tobie’s proposal on CommonJS with mine on UncommonJS. The latter proposal is as you suggest.

And I misread the UncommonJS/Modules ones from before. It had the exports passed to the "success" case. If you allow multiple IDs, then you get the AMD require:

    require(['id1', 'id'], function (ID1, ID2) {});

That require does not have an error callback, but it has been brought up on the amd-implement list in some forms before, so I can see how that modification could make it into that require.

Anyway, just some compare/contrast notes.

@kriskowal Your assumption about statically linked dependencies for the async loaded module holds true for me too. So far I have not needed to load more than one module at a time.

Why require.async() vs module.async() or module.load()? CommonJS/Modules/2 (draft) suggests module.load() for reasons I don't 100% remember but there is a thread about it.

I am currently using:

module.load(mappedID, function(canonicalID) {
    var ID = require(canonicalID);
})

Where mappedID is a package local ID that will resolve first term against package mappings or it can be a relative ID. Having canonicalID ID has been useful. Exports are not always needed right away. I have the need for the error callback as well so will likely add that as third argument.

@cadorn As for the name, it was a bike-shed because some participants felt that it didn’t read well. I disagree. Your current form resembles Tobie’s Async/A Draft 2 Proposal, and shares the flaw that the inner require is not easily distinguishable from an outer, static, require. UncommonJS/Modules are still firmly in the world of statically analyzing require() calls instead of declaring them up front.

My thought about the load name is that it would load but not execute. require has always implied async preload and execute.

the inner require is not easily distinguishable from an outer

But it is. One takes a string literal as argument and the other does not.

UncommonJS/Modules are still firmly in the world of statically analyzing require() calls instead of declaring them up front.

As mentioned in comment above I also statically analyze.

I agree with the load and execute distinction. The only case where that should matter though is where a module acts on the global environment (which you may want to happen at a precise time) vs its own exports only. Is that a fair assumption? Under what other conditions would you want to load and not execute right away?

@cadorn I see, regarding statically analyzable. I still do not think I could sell someone on lacing it through when it’s simpler to just provide the exports. If someone wants to load and execute lazily, the spec can grow a require.load(id, cb, eb) just as well. I can’t think of a case where I would lazily load and not immediately execute. Global patching really should happen outside the purview of the module system, or at least before anything else is executed. There are a lot of ways to guarantee that.

Global patching really should happen outside the purview of the module system

Right. Exactly my point and hence no real need to have the load/execute separate.

So the UncommonJS/Modules variant works for me as long as I can get the canonical id of "id" in the success callback but that can happen via a separate look up method so no problem there.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.