Search code examples
javascriptrequirejsamd

Render each module separately after finished loading


I have an application with multiple modules that need to be rendered as soon as they have finished loading. The idea is that each module performs its dedicated render function regardless of other modules' states.

Here's the implementation of the main module:

;(function(doc, win, undefined) {
   'use strict';

   define(['module', 'domReady!'], function(main) {
      var modules = main.config().modules;
      while (modules.length) {
         require([].concat(modules.shift()), function(module) {
            module.init();
         });
      }
   });

}(document, window));

It seems that despite the fact that each module should render right after it's been loaded, nothing appears on the screen before all the modules have finished loading. This causes lag when entering the app and doesn't give indication that the app has started executing. What might cause this kind of behavior and how to get the initialization of the app as smooth as possible?


Solution

  • It is not part of the AMD semantics to provide the effect you are looking for. The only guarantee you have is that the callback you pass to require won't run until the modules listed in the dependency array that goes with the callback, and their dependencies, are loaded.

    RequireJS does not have fine control on how the browser sends the requests to the server. For instance, if it so happens that by the time you issue your requests, the browser already has too many other requests pending (for stylesheets, images, or other things that it must load besides the modules you asked for through require), the browser may decide to delay RequireJS' requests until earlier ones are resolved.

    How the requests are resolved on the server end also affects what will happen in what order.

    Then there's the structure of your application. Suppose that main.config().modules contains ['A', 'B', 'C']. So you want to load A, B, C in order and have them initialize ASAP. However, it turns out that all these modules also depend jquery. When RequireJS executes A, it run define(['jquery], function ($) {.... Okay, so now jquery must be fetched. By the time jquery has been fetched, the likelihood is that B and C will also be waiting for it. So RequireJS will execute A, B, C and the callbacks waiting for them in close successive order. With just one dependency it may not be a big deal but if your modules depend on a slew of other common modules, it adds up and the end result is that you get a noticeable wait while the common modules are loaded and then it looks like A, B, C execute in a batch.

    It is possible by using RequireJS' own optimizer or another optimizer that understands AMD modules to try to combine your modules into bundles that will favor the result you are looking for. Doing this kind of optimization requires a developer to carefully analyze the structure of the application. In the general case, there's no single flag, option or plugin that you can just turn on to get the result you are after. Moreover, as mentioned above browser and server may work in a way that will muck it all up.


    One thing I should mention for completeness. You can force a sequential initialization but requesting one module only after the previous one has loaded:

    ;(function(doc, win, undefined) {
       'use strict';
    
       define(['module', 'domReady!'], function(main) {
          var modules = main.config().modules;
          function next() {
            var name = modules.shift();
            if (name === undefined) {
              return;
            }
    
            require([name], function (module) {
              // When one module has loaded, we initiate the request for the
              // next one.
              next();
    
              // And init what we've got now.
              module.init();
            });
          }
    
          next();
       });
    }(document, window));
    

    However, the cost here is that B is requested only after A has loaded, etc. Whereas in your original code, all modules are requested right off the bat. This may result in a total initialization time which is longer.