My understanding is that it shouldn't happen, but it does. What is the most appropriate workaround for these libraries?
Notes:
require.js
manually (using a script tag) should work, and indeed it seems like it does. However, the RequireJS documentation explicitly warns that the data-main
script is run asynchronously. While require.js
should have properly defined the define
function needed by the scripts defining multiple named modules, I also assume that without the proper configuration loaded from the data-main
script, bad things may happen in a non-deterministic way. Is this correct?shim
, map
, bundles
and paths
configuration properties can help in this case, although I hope I'm missing it.Clarification on the first note: (My bad, it's really not clear)
What I describe here is simply to manually execute (using an HTML script tag) the script that defines multiple modules after RequireJS and the data-main
script. With the knowledge that the latter is run async, my worries should become more obvious (but feel free to ask me to detail some more). The bulk of it is that although it seems like I can require each named module successfully, I'm not sure that the behavior is deterministic (also, it's not pretty, I'd much rather avoid additional script tags and properly load everything asynchronously).
<script src="scripts/require.js" data-main="app/main.js"></script>
<script src="scripts/datajs-1.1.2.js"></script>
Here, datajs-1.1.2.js defines two modules, as described in the link above and copied below:
// AMD support
if (typeof define === 'function' && define.amd) {
define('datajs', datajs);
define('OData', odata);
} ...
RequireJS uses Asynchronous Module Loading (AMD) for loading files. Each dependent module will start loading through asynchronous requests in the given order. Even though the file order is considered, we cannot guarantee that the first file is loaded before the second file due to the asynchronous nature.
RequireJS is a JavaScript library and file loader which manages the dependencies between JavaScript files and in modular programming. It also helps to improve the speed and quality of the code.
So, RequireJS doesn't support it. From your use case it seems that you don't need synchronous RequireJS, you need to return result asynchronously. AMD pattern allows to define dependencies and load them asynchronously, but module's factory function must return result synchronously.
The define() function can be used to load the modules (module can be an object, function, class or a code which is executed after loading a module). You can load different versions of the same module in the same page.
What will and will not work depends on the specifics of how the file which defines multiple named modules will be used in an application.
In general, if the order in which modules defined (using named defines) in a single file cannot be determined, then setting paths
to map the module names to the file that defines them should prevent problems:
paths: {
'foo': 'path/to/foobar',
'bar': 'path/to/foobar'
}
If foo
or bar
are required, RequireJS will load the file that defines both (path/to/foobar.js
), which is not a problem.
With the details that you've added to the question, I can say this. First, this code:
<script src="scripts/require.js" data-main="app/main.js"></script>
<script src="scripts/datajs-1.1.2.js"></script>
is incorrect. Loading a module that calls define
through a <script>
tag is generally wrong. (I would say it is always wrong, but there may be some really strange cases where to get incompatible assets to work together you have to do something that would normally be wrong. But this is unusual, and has to be justified.) As you suggested by doing this, you open yourself to timing issues. Sometimes it may work, sometimes it may not.
However, this should prevent any timing issues:
<script>
require = {
paths: {
datajs: 'scripts/datajs-1.1.2',
OData: 'scripts/datajs-1.1.2'
}
};
</script>
<script src="scripts/require.js" data-main="app/main.js"></script>
Whenever anything needs either of the two modules in datajs-1.1.2.js
, either because it called require
or because it called define
with the appropriate module names, the file that defines both modules is going to be loaded.
(Warning: the configuration I show in the example above is an educated guess, which contains enough details to illustrate. It may not work once combined with a configuration already present in app/main.js
, and I'm not suggesting that it is the best way to configure RequireJS for your specific application.)
For RequireJS version 2.1.10 and higher, there's also the bundles
option, which is nicer to use:
<script>
require = {
bundles: {
"js/datajs-1.1.2": ["datajs", "OData"]
}
};
</script>
<script src="scripts/require.js" data-main="app/main.js"></script>
I suggest reading the documentation on this option to avoid possible misunderstandings as to how it works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With