2
votes

I'm writing a modular web app using RequireJS for module loading and dependency injection.

From my bootstrap.js file I load Application.js and initialize it, passing in an array of modules that are to be "loaded"(1) by the Application. When the initialization finishes, the Application will call a function to signal that it's done loading.

I'm loading the modules asynchronously (in regard to each other) using require(["module"], callback(module), callback(error)).

The problem that I'm having with this is that the error callback (errback) is not called when a module fails to load (at least on Chrome when the server responds with a 404 status code).

I can see the error in the Google Chrome Developer Tools Console, but the errback isn't called:

GET http://192.168.1.111:8812/scripts/modules/InexistentModule/manifest.js 404 (Not Found) 
Uncaught Error: Load timeout for modules: modules/InexistentModule/manifest 

Has anyone experienced gotten around this issue with RequireJS errbacks? If so, how?

(1) Actually, I'm just loading module manifests, not the entire modules, so that I can display icons for them and register their routes using Backbone.SubRoute

The libraries that I'm using (none of them are minified):

  • RequireJS 1.0.8
  • Underscore 1.6.0
  • jQuery 1.11.0
  • Backbone 1.1.2
  • Backbone.SubRoute 0.4.1

Out of the libraries above, only RequireJS and Underscore are used directly by me at the moment.

I've used Underscore for currying when passing the success/failure callbacks to Require in order to pass in the i from my loop as the index parameter. For the success callback, this works wonderfully and I think that this does not affect the errback (I've tested with a simple function of arity 1, instead of the partial function returned by _.partial and the function is still not called in case of a 404 error).

I'll post my bootstrap.js and Application.js files here as they might provide more info on this.

Thank you!

bootstrap.js

require.config({
    catchError: true,
    enforceDefine: true,
    baseUrl: "./scripts",
    paths: {
        "jquery": "lib/jquery",
        "underscore": "lib/underscore",
        "backbone": "lib/backbone",
        "backbone.subroute": "lib/backbone.subroute"
    },
    shim: {
        "underscore": {
            deps: [],
            exports: "_"
        },
        "backbone": {
            deps: ["jquery", "underscore"],
            exports: "Backbone"
        },
        "backbone.subroute": {
            deps: ["backbone"],
            exports: "Backbone.SubRoute"
        }
    }
});

define(["jquery", "underscore", "backbone", "Application"],
    function ($, _, Backbone, Application) {
        var modules = ["Home", "ToS", "InexistentModule"];
        var defaultModule = "Home";

        var onApplicationInitialized = function()
        {
            require(["ApplicationRouter"], function(ApplicationRouter){
                ApplicationRouter.initialize();
            });
        }

        Application.initialize(modules, defaultModule, onApplicationInitialized); 
    }
);

Application.js

define([
    'jquery',
    'underscore',
    'backbone'],
function($,_,Backbone){
    var modules;
    var manifests = [];
    var routers = [];
    var defaultModule = "";

    var totalModules = 0;
    var loadedModules = 0;
    var failedModules = 0;
    var onCompleteCallback = function(){};

    var onModuleManifestLoadComplete = function(index, manifest){
        manifests[index] = manifest;
        console.log("Load manifest for module: " + modules[index] + " complete");
        //TODO: init module 
        loadedModules++;
        if(totalModules == (loadedModules + failedModules))
            onCompleteCallback();
    };

    var onModuleManifestLoadFailed = function(index, err){
        console.log("Load manifest for module: " + modules[index] + " failed");
        failedModules++;
        if(totalModules == (loadedModules + failedModules))
            onCompleteCallback();
    }; 

    var initialize = function(_modules, _defaultModule, callback){
        defaultModule = _defaultModule;
        modules = _modules;
        manifests = Array(modules.length);
        totalModules = modules.length;
        onCompleteCallback = callback;

        for(i=0; i<modules.length; i++){
            require(['modules/'+modules[i]+'/manifest'], 
                _.partial(onModuleManifestLoadComplete, i),
                _.partial(onModuleManifestLoadFailed, i));
        };
    };

    return {
        modules: modules,
        manifests: manifests,
        routers: routers,
        defaultModule: defaultModule,
        initialize: initialize
    };
});
1
Yes, the onModuleManifestLoadComplete works and it gets called for every one of the existing manifest.js files after they've loaded. - lucian.pantelimon
If all the modules exits the onCompleteCallback gets called too and the Router in bootstrap.js gets initialized. The problem appears when at least one module fails to load. - lucian.pantelimon
If you test with only one non-existing module to be loaded by the code you show in your question, does the errback get called as you'd expect? Also (this is a long shot but some questions on SO were resolved this way, so...) are you running the very latest version of RequireJS? - Louis
I've just tested with only one inexistent module and the errback does not get called. I've added the library versions to my question. - lucian.pantelimon

1 Answers

6
votes

You indicate you are using RequireJS 1.0.8. I've checked the documentation for the 1.x series and find nothing about errbacks. This page actually indicates that errbacks were introduced in the 2.x series.

Also the shim is something that was introduced in the 2.x series. So right now, RequireJS is ignoring your shims.