public

A sample showcasing some of the things RequireJS can and can not do.

  • Download Gist
requirejs-nested-require-calls.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
//let's say you have these three AMD modules:
 
//testmodule1.js
define(function() {
var val = { iAm:'testmodule1.js' };
console.log(val);
return val;
});
 
//testmodule2.js
define(function() {
var val = { iAm:'testmodule2.js' };
console.log(val);
return val;
});
 
//testmodule3.js
define(function() {
var val = { iAm:'testmodule3.js' };
console.log(val);
return val;
});
 
//#1 let's start with a standard AMD type module definition which requires above test modules
define(['testmodule1', 'testmodule2', 'testmodule3'], function(testmodule1, testmodule2, testmodule3) {
return {};
});
 
/* As expected, console says
testmodule1 Object {iAm: "testmodule1.js"} testmodule1.js:4
testmodule2 Object {iAm: "testmodule2.js"} testmodule2.js:4
testmodule3 Object {iAm: "testmodule3.js"} testmodule3.js:4
*/
 
//#2 Let's throw some synchronous nested "require" calls in there:
define(['testmodule1', 'testmodule2', 'testmodule3'], function(testmodule1, testmodule2, testmodule3) {
 
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
 
/* Looks like the synchronous require calls worked!
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
Object {iAm: "testmodule1.js"} Object {iAm: "testmodule2.js"} Object {iAm: "testmodule3.js"} requiretest.js:3
*/
 
//#3 But what if we don't import the modules in the define header?
define([], function() {
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
 
 
/* Console tells us:
Uncaught Error: Module name "testmodule1" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded require.js:160
*/
 
//So we can only use the "synchronous" require calls for modules which have already been loaded.
 
//#4 What if we use the CommonJS sugar provided by requirejs?
define(function(require, module, exports) {
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
 
 
/* Looks like the synchronous require calls worked again!
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
Object {iAm: "testmodule1.js"} Object {iAm: "testmodule2.js"} Object {iAm: "testmodule3.js"} requiretest.js:3
*/
 
//#5 Let's mix it up, and load one module conditinally (if(false)... so never) and one module
// only when we need it...
define(function(require) {
 
console.log('testmodule1', require('testmodule1'));
 
if(false) {
console.log('testmodule2', require('testmodule2'));
}
 
function Foo() {
this.bar = function() {
var testmodule3 = require('testmodule3');
console.log('testmodule3', testmodule3);
};
}
 
return {};
});
 
 
/* And surprisingly, all the modules were loaded, even though only one "require" call was executed...
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
testmodule1 Object {iAm: "testmodule1.js"} requiretest.js:3
*/
 
//This behavior is because when using the CommonJS syntax, requirejs parses the code file
//for nested require calls and loads all the found modules. For that reason you can't use
//variable names or non-literal string constructs in require calls.
 
//#6 For example:
define(function(require) {
var moduleName = 'testmodule1';
console.log('testmodule1', require(moduleName));
});
 
/* Will not work
Uncaught Error: Module name "testmodule1" has not been loaded yet for context: _.
http://requirejs.org/docs/errors.html#notloaded require.js:160
*/
 
//We can see the module resolution in action when we build the example #5 with r.js (with optimize:none).
 
//#7 We get:
define('requiretest',['require','testmodule1','testmodule2','testmodule3'],function(require) {
 
console.log('testmodule1', require('testmodule1'));
 
if(false) {
console.log('testmodule2', require('testmodule2'));
}
 
function Foo() {
this.bar = function() {
var testmodule3 = require('testmodule3');
console.log('testmodule3', testmodule3);
};
}
 
return {};
});
 
//RequireJS pulled all the nested require calls and lifted them to the top of the module.
//The compiled code works for the same reason #2 works - because RequireJS has already loaded
//and cached the modules, and can simply return cache[moduleName].
 
//#8 It's possible to lazy load modules using the require(['modulename'], function(module) {}); syntax.
define(function(require) {
 
console.log('testmodule1', require('testmodule1'));
 
if(false) {
require(['testmodule2'], function(testmodule2) {
console.log('loaded', testmodule2);
});
}
 
if(true) {
require(['testmodule3'], function(testmodule3) {
console.log('loaded', testmodule3);
});
}
 
return {};
});
 
/* Only the evaluated dependencies are loaded, and in correct order
Object {iAm: "testmodule1.js"} testmodule1.js:4
testmodule1 Object {iAm: "testmodule1.js"} requiretest.js:3
Object {iAm: "testmodule3.js"} testmodule3.js:4
loaded Object {iAm: "testmodule3.js"} requiretest.js:13
*/
 
//#9 As long as you use a string literal in the module definition, the build will work too. Here's output,
// where only the first module is evaluated at the top:
define('requiretest',['require','testmodule1'],function(require) {
 
console.log('testmodule1', require('testmodule1'));
 
if(false) {
require(['testmodule2'], function(testmodule2) {
console.log('loaded', testmodule2);
});
}
 
if(true) {
require(['testmodule3'], function(testmodule3) {
console.log('loaded', testmodule3);
});
}
 
return {};
});
 
/* But so in short:
1. The 'require' method is technically speaking synchronous, but can't be safely used,
because the modules may not be loaded in the context.
2. With the CommonJS sugar syntax you can call `require` safely, but you should know that
the modules are still loaded up front, unconditionally. Loading modules lazily with
var foo = require(bar) syntax is not possible.
3. RequireJS is magic.
*/

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.