Skip to content

Instantly share code, notes, and snippets.

@Integralist
Created January 23, 2012 14:35
Show Gist options
  • Save Integralist/1663422 to your computer and use it in GitHub Desktop.
Save Integralist/1663422 to your computer and use it in GitHub Desktop.
Beware of setting 'require' as a dependancy

Imagine this is your module…

define(['Utils/css'], function(css){

	console.log(css);
	
});

If your Utils/css dependancy was like the following…

define(['require'], function(require){

	return {
		style: require('Utils/CSS/getAppliedStyle'),
		classes: require('Utils/CSS/getArrayOfClassNames'),
		add: require('Utils/CSS/addClass'),
		remove: require('Utils/CSS/removeClass'),
		has: require('Utils/CSS/hasClass')
	}

});

…then you would have this error displayed: Uncaught Error: Module name 'Utils/CSS/getAppliedStyle' has not been loaded yet for context: _ because you created a race condition issue.

One work around would be to do…

define(['Utils/CSS/getAppliedStyle', 'Utils/CSS/getArrayOfClassNames', 'Utils/CSS/addClass', 'Utils/CSS/removeClass', 'Utils/CSS/hasClass'], function(getAppliedStyle, getArrayOfClassNames, addClass, removeClass, hasClass){

	return {
		style: getAppliedStyle,
		classes: getArrayOfClassNames,
		add: addClass,
		remove: removeClass,
		has: hasClass
	}

});

But that's FUGLY, so instead, go back to what we had before but just don't specify require as a dependancy…

define(function(require){

	return {
		style: require('Utils/CSS/getAppliedStyle'),
		classes: require('Utils/CSS/getArrayOfClassNames'),
		add: require('Utils/CSS/addClass'),
		remove: require('Utils/CSS/removeClass'),
		has: require('Utils/CSS/hasClass')
	}

});

I have an idea of why this works but maybe @jrburke can clarify.

@millermedeiros
Copy link

the last example works because it switches RequireJS behavior.. it understands that the module is written in the "simplified" CJS format..

when you list any dependencies (even if it is require, exports or module) it goes back to the default AMD async behavior and the synchronous require will only work if module was already loaded for that context.

@millermedeiros
Copy link

forgot to say, I would probably use relative paths like here

@Integralist
Copy link
Author

@millermedeiros thanks for the advise re: relative paths :-) but can you clarify your last point "synchronous require will only work if module was already loaded for that context". Maybe if you could give me an example of what you mean please.

@millermedeiros
Copy link

define(['require', 'Utils/CSS/getAppliedStyle'], function(require){
  return : {
     // this will work since "Utils/CSS/getAppliedStyle" was loaded before
     style : require('Utils/CSS/getAppliedStyle')
  }
});

it would work even if the module "Utils/CSS/getAppliedStyle" was loaded by another module, RequireJS creates another context only if you use the multiversion feature, but by default all modules are loaded on the root (_) context - requirejs.s.contexts._.defined contains a reference to all the defined modules on the _ context, so when you require('foo') it will check if the module foo is on this list and if so return it, otherwise throw the error you saw on the first example.

here is how requirejs does the CJS "magic": https://github.com/jrburke/requirejs/blob/4cf2e3fcb4/require.js#L1673-1694 - basically if you don't have a dependency array it checks the number or parameters and reads the function content to grab the dependencies.

@Integralist
Copy link
Author

@millermedeiros so to clarify: no dependencies and passing require as argument means modules are loaded synchronously. But the downside is that there is no check to first see whether the require'd module(s) have already been loaded. Where as the AMD async require() calls will check internally to make sure we're not double loading.

@millermedeiros
Copy link

the flow is basically:

no dependencies and +1 parameter define(function(require){ ... })

  1. call toString() on the callback function and read all the require('module_id') calls.
  2. load all the dependencies asynchronously.
  3. execute callback.

no dependencies and no parameters define(function(){ ... });

  1. execute callback

dependency array define(['require'], function(require){ ... })

  1. load all the dependencies on the array asynchronously.
  2. execute callback.

so as you can see both loads the files asynchronously but the later doesn't check the require('module_id') since it considers that all the dependencies are already on the array and/or all the internal require calls are asynchronous or are requiring modules that was previously loaded.

if you want the require('module_id') to work as if it was a CJS module you need to use the 1st pattern or use the full CJS wrapper (2-3 arguments):

define(function(require, exports, module){
  exports.foo = require('./foo');
});

@millermedeiros
Copy link

I just created a new page on the RequireJS wiki explaining the difference between the module formats and what RequireJS does internally: https://github.com/jrburke/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define

@Integralist
Copy link
Author

@millermedeiros thanks for the 'flow' style explanation that worked a lot better for me :-)
One other query though, why do you recommend using relative paths?
e.g. define(['./css'], function(css){ over define(['Utils/css'], function(css){
Is there a benefit that I'm not aware of, or is that just your personal preference?
I thought it would be 'clearer' the way I have it if I was to move this module from one project to another.

@millermedeiros
Copy link

since both will end up pointing at the same place I usually use relative paths to reference things that are inside the same folder, if you change the folder structure or decide to rename folders you don't need to go back and update everything. It's also more concise.

@Integralist
Copy link
Author

@millermedeiros actually yes the folder renaming thing could be a pain if not using relative paths. That's good enough for me, I'll change them :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment