0
votes

I'm running into a variety of issues with race conditions and scoping issues with my web application, using require.js for managing dependencies.

I've been attempting to clean up and optimise the codebase, and introduce r.js as a mechanism for controlling the huge number of requests being generated on page loads (loading dependencies).

Question:

What are the consequences of failing to declare Parameters explicitly within either a module definition like this:

define([
    "jquery",
    "app/modules/validation",
    "jquery.chosen"
], function(
    $,
    Validation,
    // note missing parameter
) { //stuff here });

Or a require statement like this:

require([
    "jquery",
    "app/modules/validation",
    "jquery.chosen"
], function(
    $,
    Validation,
    Chosen
) {
    // the 'chosen' object here is actually referencing the object defined in the other module
});

I'm finding numerous issues in my application with scoping and I'm wondering if the above might be to blame? It seems like failing to include a parameter allows it to leak scope? Is this possible?

Edit I was trying to avoid this turning into a 'debug my require.js' configuration but here we go:

My config.js

requirejs.config({
    paths: {
        jquery: 'jquery-shim',
        'jquery.chosen':'chosen/chosen.jquery',
        'jquery.validity': 'lib/jquery.validity'
    },
    shim: {
        'jquery.chosen': ["jquery"],
        'jquery.validity': {
            deps: ["jquery"],
            exports: "jQuery.fn.validity"
        }
    }
});

as you can see jQuery is currently aliased to the following - I think this might be the problem:

//jquery-shim.js     

 define([], function() {
    return window.jQuery;
 });

When I load require.js in my html it looks like this: As you can see jquery is being loaded outwith require.js - the reason for this is that when loaded with require.js, it does not bind to the DOMContentLoaded event, and hence all other code that binds to jQ.ready, never executes:

<script src="{{ asset('assets/js/jquery/dist/jquery.min.js') }}"></script>
<script src="{{ asset('assets/js/requirejs/require.js') }}"></script>
<script>
require(['app/site_core'], function (common) {
    require(['app/default']);        
});
</script>
2
I think the dependency is still passed in as an argument. You're just not giving it an identifier.SimpleJ
That was my understanding initially but I've been questioning it and I wanted to confirm. If this is the case add it as an answer and I'll close the question/mark you correct.calumbrodie
I also found requirejs hard to use with external libraries and vice-versa. That is why I created a library which is much easier to use and is tested with angular. There is a demo application at the bottom: gngeorgiev.github.io/Modulerr.jsGeorgi-it
I also found requirejs hard to use with other libraries and vice-versa. That is why I created a library which is much easier to use and is tested with angular. There is a demo application at the bottom: gngeorgiev.github.io/Modulerr.jsGeorgi-it

2 Answers

1
votes

This largely depends on what your "stuff here" is. Since you're not including this information I'll try to cover all combinations:

  1. You're requiring jquery.chosen, you're binding it to the factory function parameter (e.g. "Chosen") and you use it. This is the normal usage pattern.
  2. You're requiring jquery.chosen, you're binding it to the factory function parameter (e.g. "Chosen") but you're not using it inside the function. Remove this from the dependency list - there's no need to clutter the module signature.
  3. You're requiring jquery.chosen, you're not binding it to the factory function parameter but you don't use this dependency anyway. Similar to 2.: remove all unneeded dependencies to keep your code clean.
  4. You're requiring jquery.chosen, you're not binding it to the factory function parameter but you're using it. This is a potentially fatal scenario, depending on how the plugin is written it could work some of the time because JavaScript engine will try to find the specified variable looking at all known scopes, starting at local and ending on window scope. You're lucky it will fail immediately, but if you picked a variable name that exists in the global scope you'll use it skipping the AMD mechamisms, causing unexpected quirks and timing-sensitive issues.

Last two variants are also dangerous because you may not notice the mismatch when adding a new dependency later, causing confusing errors, e.g.:

define([
  "jquery",
  "app/modules/validation",
  "jquery.chosen",
  "underscore",
], function($, Validation, _) {
  //_ variable refers to jQuery.chosen due to the mismatch
});

Not sure what you mean by "the other module" in the last comment: "the 'chosen' object here is actually referencing the object defined in the other module"

0
votes

What are the consequences of failing to declare Parameters explicitly

None whatsoever, until you want to use it inside your function but can't because you have no reference to it. RequireJS will not object nor will it be put in some problematic state because you're missing a parameter. There are legitimate use cases of AMD modules with no return values, whose effect is some kind of side effect such as the decoration of a different AMD module.

Without seeing any code, I can hazard a guess that your problems are most likely due to your jquery plugins not properly loading jQuery within AMD -- most likely there's a bad use of noconflict, a failure to specify load order via shims, or issues with globals. See RequireJS's instructions for jQuery for more details.

EDIT: There doesn't seem anything obviously wrong with your config, except I don't see a reference to the layer produced by r.js, unless app/site_core is your layer. You should make sure you require the layer before you require the modules contained within it. The RequireJS config can be used to do this elegantly.

The following is modified from RequireJS documentation. Note that the layer is specified as a dependency.

var require = {
    deps: ["my/layer"],
    callback: function(module1, module2) {
        //This function will be called when all the dependencies
        //listed above in deps are loaded. Note that this
        //function could be called before the page is loaded.
        //This callback is optional.
    }
};