Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 243
Cosmin SD Avatar asked Nov 10 '13 17:11

Cosmin SD


People also ask

Is RequireJS obsolete?

RequireJS has been a hugely influential and important tool in the JavaScript world. It's still used in many solid, well-written projects today.

Is RequireJS synchronous?

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.

Why do we need RequireJS?

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.

What is Shim RequireJS?

shim: Configure the dependencies, exports, and custom initialization for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value. Here is an example. It requires RequireJS 2.1. 0+, and assumes backbone. js, underscore.


2 Answers

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();
        [...]
    });

    [...]
});
like image 117
Louis Avatar answered Sep 22 '22 10:09

Louis


It depends on what loading behavior you want.

When you require a module that has not been required yet, a HTTP request will be made to load the required module. If the required module has been loaded atleast once, it is stored in require's cache, and so when you require the module the second time, it will use the cached value.

So, its a choice between lazy or eager loading.

The problem that require is trying to solve, is that each module can define it's dependencies - so your first approach would probably be "best practice". In the end, you'd want to use r.js to build your application into a single JS file.

like image 27
Jeff Avatar answered Sep 20 '22 10:09

Jeff