Search code examples
javascriptperformancerequirejsamd

requirejs - Performance of multiple calls to require


I'm wondering what is the proper approach for using RequireJS in a project with multiple modules, regarding performance of multiple require calls with less dependencies vs a single require with all the dependencies.

Let's take the case where, for an app, I need to load some modules: gmaps, jquery, module1, module2, module3. The use for some of the modules is quite independent. So, the question is which of the following alternatives is recommended (supposedly this code is the main module loaded into the page):

require(['gmaps'], function(gmaps){
   gmaps.use();
});

require(['jquery','module1'], function(jquery, module1){
   module1.use();
});

require(['jquery','module2'], function(jquery, module2){
   module2.use();
});

require(['jquery','module3'], function(jquery, module3){
   module3.use();
});

vs

require(['jquery','module1','module1','module1','gmaps'], function(jquery, module1,module2,module3,gmaps){
   module1.use();
   module2.use();
   module3.use();
   gmaps.use();
});

In other words, what is the performance penalty of require and which is the best practice.


Solution

  • The answer to the question here is "it depends." I'm speaking as someone who has used RequireJS in a large application but who has not thoroughly read the code of RequireJS. (Just pointing out that people who know the innards of RequireJS inside and out might explain differently than I do.) The cost of require can be broken down into 3 cost scenarios:

    1. If the module has never been loaded, require loads a file from a server, executes the file, executes the factory function for the module and returns a reference to the module. (The cost of loading files from the network usually dwarfs other costs.)

    2. If the module has already been loaded but never required, require executes the factory function for the module and returns a reference to the module. (This will normally happen in an optimized application.)

    3. If the module has already been loaded and required, require returns a reference to the module.

    Cost scenario 1 > cost scenario 2 > cost scenario 3.

    First, let's consider the case where there is one module per file. The application is not optimized. I have a module named module1 which is required once in a blue moon. It's usage in a main application could be modeled like this:

    define(["module1", <bunch of other required modules>],
        function (module1, <bunch of other modules variables>) {
    
        [...]
    
        if (rare_condition_happening_once_in_a_blue_moon)
            module1.use();
    
        [...]
    });
    

    Here I always pay the price of cost scenario number 1 even if I don't use the module. It would be better to do:

    define([<bunch of required modules>],
        function (<bunch of module variables>) {
    
        [...]
    
        if (rare_condition_happening_once_in_a_blue_moon)
            require(["module1"], function (module1) {
                module1.use();
            });
    
        [...]
    });
    

    This way, I'd pay the price of loading the module only once in a blue moon.

    Now, what if I need to use module repeatedly? This can be modeled as:

    define(["module1", <bunch of other required modules>],
        function (module1, <bunch of other modules variables>) {
    
        [...]
    
        for(iterate a gazillion times)
            module1.use();
    
        [...]
    });
    

    In this case, cost scenario number 1 is paid once, and that's all. If I use require like this:

    define([<bunch of required modules>],
        function (<bunch of module variables>) {
    
        [...]
    
        for(iterate a gazillion times)
            require(["module1"], function (module1) {
                module1.use();
            });
    
        [...]
    });
    

    I'm paying cost scenario number 1 once and a (gazillion times - 1) cost scenario number 3. At the end of the day, whether the module1 should be included among the requirements of the define call or in a separate require call depends on the specifics of your application.

    The analysis changes if the application has been optimized by using r.js or a home-made optimization. If the application is optimized so that all modules are in one file, every time you paid cost scenario 1 in the cases above, you'd pay cost scenario 2 instead.

    For the sake of completeness, I'll add that in cases where you do not know ahead of time the module you might want to load, using require is necessary.

    define([<bunch of other required modules>],
        function (<bunch of other modules variables>) {
    
        [...]
    
        require(<requirements unknown ahead of time>, function(m1, m2, ...) {
            m1.foo();
            m2.foo();
            [...]
        });
    
        [...]
    });