This is exactly the problem with trying to decouple fetching from
executing -- I have to type that 'jquery' string twice, and in this
case (the vast majority of cases), it was completely unnecessary.
require([]) is sufficient to give an indication of something that will
be used in the future but is not needed at the moment. For modules
referenced in require([]), a loader can choose to prefetch them before
those calls are actually executed, or a builder could choose to inline
them as strings for rehydration later, but I do not believe any new
module definition machinery is needed.
> Because of this, I think the script tag will move toward obsolescence and
> "async" and "defer" will be artifacts of an antiquated paradigm.
While that may be true, what is not changing is that IO in general in
the browser is async. Luckily the AMD API does not encode the
assumption of script tag use for IO, just that IO may be async.
> AMD needs to move beyond the narrow module definition spec (which it already in effect
> is) to a much broader module loading spec. In addition, how those modules
> are first loaded into memory (eval, dynamic script injection, etc...) should
> be the boundary of its concern, left to the realm of script loaders to
> innovate. This is what I would like to see, and I think the whole of the
> ecosystem would flourish with this distinction as you'll find much of the
> push back from AMD esp. within the node community revolves around the
> dislike of the asynchronous require and the need to wrap in a define. This
> is jut plain silly. Nonetheless, a porting of a node style synchronous
> require with AMD define() support would almost certainly find swift adoption
> as module loading in the browser is still at the moment unnecessarily
> painful, requiring too much configuration, along the line of node's now
> abandoned require.paths.
define() wrappers need to exist if you want to concat some modules
together. That will always be there. There needs to be a way to name a
code unit and to contain that code unit in some fashion. It is common
to deliver a set of modules in one file.
Other IO schemes may allow more finegrained control over the source so
you could delay execution, and that is fine, and it can allow for
sugar that desugars nicely to AMD.
If you want sugar to avoid a define wrapper in dev, use CJS-style,
although it really needs a callback-require for computed dependencies,
to recognize the async nature of IO in the browser, and to allow
prefetching. And loader plugins. It should "desugar" nicely to AMD
because AMD works for all loading cases. But it is also fine if a
loader understands higher level sugar.
If you want to standardize a higher level sugar, for me it is the
basics of CJS style, with callback require and loader plugin support.
Although, at this point, trying to get anything going like that is
likely to be superseded by ES harmony modules. In other words, to take
advantage of the new IO options, newer browsers are needed, probably
the same set that are on rapid updates that will likely get ES harmony
modules.
Conversely, there will be old browsers around for a while. AMD works
there, and I believe many forms of the ES harmony syntax could be
transpiled back into AMD.
I fully support doing experiments with different loading strategies. I
have a demo I want to do for one that uses stringified modules that
are all built into one file. I think it is great if people share their
strategies on this list, but it is difficult to see what exactly would
need standardization. Sharing techniques and approaches, even
documenting those approaches is useful though.
In particular, for splitting fetching from execution, loaders and
build tools that can detect the use of require([]) via source analysis
seem enough. If you do not think so though, feel free to share where
it breaks down.
James
To me, the unit tests for AMD provides enough validation of a baeline
behavior for define and require (as used inside define). We do not all
have to use the exact same code algorithm. In fact, I do not see how
that would work. I don't have the same for requirejs and almond.
Almond actually supports "ordered" and "undordered" define calls, that
necessitate a slight variation in the define implementation.
I do think though that we have enough common understanding of how
define and require works that we can naturally move "up the stack" to
loader APIs, as you mention. Maybe that is your larger point.
I guess maybe if you can list out specific things that still need to
be worked out for define and require, maybe that would help me
understand where you think we are on the the path. But to me, trying
to standardize an algorithm for define/require does not seem to be
necessary. I am not aware of that actually being a blocker to AMD
adoption. The larger issue is just educating developers on modular JS
vs using a bunch of browser globals.
If you think the deficiency with define/require is about separating
io, parse and execution, I'll expand on that more below.
> Lastly, obviously if you love async require, more power to you, but with
> async require, there's no built in means to separate IO, parse and execute.
> If you have a recommendation for how you'd like to see async require support
> this, I'd be happy to hear it. That's really my primary concern regarding
> async require.
I mean that require([]) already gives enough information to indicate
when a developer wants to possibly separate IO from parsing or
executing. Example:
define(function (require) {
var a = require('a'),
b = require('b');
return {
doSomething: function (cb) {
require(['c', 'd'], function (c, d) {
cb(c() + d());
});
});
};
});
This module needs 'a' and 'b' immediately, so they should be
fetched/executed when this module is fetched executed. However, a
loader could decide to introspect and see that 'c' and 'd' are needed
at some point. It can choose to just fetch them now and put them in
localStorage, or just hold them in strings. It could even parse them,
but not execute them, to find out that 'c' and 'd' also need 'e', and
do the IO fetch for 'e' too, but not execute it.
Then when that require([]) call is run, the loader knows to then
execute the code.
Similarly, a build tool could parse the module above, and include this
module and 'a' and 'b' in a built file, but then put 'c', 'd', and 'e'
as strings in that built file. So that a loader that understood that
built format would know how to unpack that information, likely by
having the builder include a specialized loader in that built file.
The builder could also just decide to put 'a' and 'b' as strings in
the built file too, and only evaluate them when the above module
executes, but that does not seem so useful since the current module
would need them right away.
If you think that AMD should adopt CommonJS modules 1.1. behavior of
not executing the dependency until there is an internal require(),
that goes against AMD 's define(['dep']) syntax, and how ES harmony
modules, as currently specified will work. It would also bloat the
basic API, as your previous example showed. It adds a global tax for
something that is very rarely needed.
And it does not help as much compared to require([]) when IO is async.
If any module is not needed until "later", then it really should not
even be fetched for best performance, and the use of a callback-style
require is enough of an API to signal to a loader that the dependency
is not needed right away, and that loader can decide if it wants to do
some prefetching or other analysis before the module is actually
needed.
James
* In spite of Node's popularity, the volume of browser-side JavaScript
outweighs the volume of server-side JavaScript by at least an order of
magnitude. The cjs module loader [used by Node] does not fulfill the
requirements of the larger js user set. This is a cjs mistake, not an
AMD mistake. There is no reason server-side module loading couldn't
evolve to amd, if one is looking for unification.
* It may very well be true that future brower-side JS capabilities will
render AMD obsolete. But today those solns don't exist. AMD solves the
problem and many are using it with great success.
* I fail to see how AMD uptake is being harmed in any way by it's
current design and incompat with cjs. Indeed, the async feature is the
raison d'�tre for AMD. It's not going to change.
--Rawld
On 02/09/2012 04:15 PM, James Burke wrote:
> On Thu, Feb 9, 2012 at 2:48 PM, Adam Crabtree<atcra...@gmail.com> wrote:
>> This is a good thing, but I think the inclusion of async require and loader
>> plugins is glossing over the intermediary step of resolving module loading.
>>
>> _I recognize this is a mostly semantic argument, but I feel it's a necessary
>> distinction._
>>
>> In other words, it seems that a lot of the attention around AMD loaders is
>> around IO and not about loading. The reason I bother to bring this up is not
>> bike-shedding, but for a specific purpose. That purpose is that I'd like to
>> see all AMD loaders share the same define/require implementations. _A shared
>> implementation of define()& local/sync require() would help AMD achieve