How to handle 404 errors from Requirejs

4.2k Views Asked by At

I am currently trying to identify the best way to handle different errors within a Durandal application. One scenario I am testing is where the required module does not exist, i.e.

define(['dummy'], function (Dummy) { return {}; });

where dummy does not exist. I have added the following to my main.js file directly below requirejs.config({...}):

requirejs.onError = function (err) {
    console.log('Global error', err);
};

but the error is never logged from here. The 404 error is being shown in the console window from Durandals system.js file and the error message is also logged:

Uncaught Error: Failed to load routed module (viewmodels/features/errorHandling/scriptNotFound/one). Details: Script error for: dummy

It looks like the requirejs.onError function never gets a chance to be called. I would like to be able to log these errors and inform the user that there was a problem etc. Anybody any idea how I would do this?

5

There are 5 best solutions below

1
On

Try making "onError" lowercase like "onerror" (see http://requirejs.org/docs/errors.html#scripterror).

2
On

There's probably no need to handle AMD dependencies load errors, because typically the application would be bundled before going into production.

Error handling as described in http://requirejs.org/docs/api.html#errors deals mostly with jsonp calls to external resources. In addition error handling has some restriction with IE, so that might explain the results that you're seeing.

0
On

I think the best way is to use Durandal's system.error function. Just put something like this somewhere in your appstart:

system.error = function(e) {
  //log the error
  console.debug(e);
  //redirect to a 404 view if you couldn't load module
  if (e.indexOf("Failed to load routed module") > -1) {
    location.href = '#404';
  }
  throw e;
};

On larger projects you may not want to bundle your entire application into a single package so you can load parts of your app incrementally or on demand so as to decrease initial load time in which case you would want to be able to handle load errors.

0
On

A common use case for this is to use a CDN-hosted version of a library, but if that fails, switch to loading the file locally:

requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
    }
});

//Later
require(['jquery'], function ($) {
    //Do something with $ here
}, function (err) {
    //The errback, error callback
    //The error has a list of modules that failed
    var failedId = err.requireModules && err.requireModules[0];
    if (failedId === 'jquery') {
        //undef is function only on the global requirejs object.
        //Use it to clear internal knowledge of jQuery. Any modules
        //that were dependent on jQuery and in the middle of loading
        //will not be loaded yet, they will wait until a valid jQuery
        //does load.
        requirejs.undef(failedId);

        //Set the path to jQuery to local path
        requirejs.config({
            paths: {
                jquery: 'local/jquery'
            }
        });

        //Try again. Note that the above require callback
        //with the "Do something with $ here" comment will
        //be called if this new attempt to load jQuery succeeds.
        require(['jquery'], function () {});
    } else {
        //Some other error. Maybe show message to the user.
    }
});

http://requirejs.org/docs/api.html#errbacks

0
On

Brandon is correct. When you use Durandal's router module it calls Durandal's system.acquire which returns a promise that will be called when Require completes loading the module. Require's errback function is provided by Durandal and takes priority over requirejs.onError. When Durandal gets an error it rejects the promise and thus the fail function of Durandal's router is called. All this fail function does is call system.error. Thus when using Durandal, you can capture all the Require module loading errors by providing your own version of system.error.