Question

I am currently developing a web application to edit some custom file formats (a small editor offering an interface to edit configuration files). A key element of this app is an object called "FileReader" that is a wrapper who triggers the right interface according to the detected type of config file. This way I just spawn a new FileReader( src ) - src being an url or a blob and it keeps clean.

Once the type of the config file is retrieved, the wrapper checks if it has an interface associated to it and simply use it. Each interface (for each different config type) is defined in a separate JS file. Once the wrapper is fully loaded, I hook all the events and the execution of app begins.

Here is the HTML skeleton I used with the relevant elements :

<!doctype html>
<html><head></head>
<body>
    <div id="UI"></div>
    <script src="js/fileReader/fileReader.js" >App.FileReader = function(){ ... }</script>
    <script src="js/fileReader/configType1.js" >App.FileReader.registerFileType("config1",object)</script>
    <script src="js/fileReader/configType2.js" >App.FileReader.registerFileType("config2",object)</script>
    <script src="js/fileReader/configType3.js" >App.FileReader.registerFileType("config3",object)</script>
    <script src="js/main.js" >App.run();</script>
</body>
</html>

Now, the app grew up and I decided to convert my app to use requirejs. My problem are mostly about "what's the best organisation" to deal with those modules because I can only consider that the FileReader is ready to use once all the file-type modules are loaded but I can't load them first because they need the mainWrapper to register their type.

The best structure I could come up with is :

main.js :

require(['require','FileReader','./fileReader/config1','./fileReader/config2','./fileReader/config3'],
    function( require ) {
        require(['require','./core/app'], function( require , App ) {
            App.run( window ) ;
        });
    });
});

fileReader.js :

define(function () {

    ...

    return FileReader ;

});

config1.js :

define(['FileReader'], function ( FileReader ) {

    var Interface = ... ;

    ...

    App.FileReader.registerFileType("config1",Interface)

    return FileReader ;

});

app.js :

define(['FileReader'], function ( FileReader ) {

    var App = function(){} ;

    App.run = function(){ FileReader.openFile( ... ) ; ... } ;

    return App ;

});

What's the point of my problem ? To get sure that the FileReader object contains the right Interface objects, I first force requirejs manually to load all the FileReader related files and THEN load the main app file. This way, once the App object requests for the FileReader object, it already contains the right Interfaces registred. What I'd like to achieve is just to only request for "./core/app" in the main file "./fileReader" in the core/app file and then during it's first load, it would be fine if it could load the config-type modules IN the fileReader file, register all the stuff and only then returns the answer.

What I tried was : (in FileReader)

fileReader.js :

define(["require"],function ( require ) {

    ...

    require(["config1","config2","config3"],
    function(){
        //occurs asynchronously, after the App.run() is triggered because of the synchronous return 
        callback(FileReader) ; //unfortunately I did not find any callback of this sort
                               //if it existed I could asynchronously find the list of the modules in the directory, load the them, and only then fire this fictive "ready" event
    }

    return FileReader ; //the first return occurs synchronously, so before the the fileReader is ready : modules are missing

});

So what is the best way to load this sort of modules (with some sort of circular dependency between the the config-type files and the the main FileReader) ? What'if I'd like to force it to be asynchronous so I can read what's available in the config-type directory ? With the simple HTML scripts list they were loaded in the right order and I had an easy access to the modules lists, now I'd like to be sure that everyting is ready before the app starts.

Was it helpful?

Solution 2

Louis' answer was interesting but it just moved the problem creating a new module, and even if it placed the dependencies in the right context, I tried to search for some real asynchronous module definition (not just loading).

The best I could finally come up with was to edit the require.js source file to add the behaviour I expected. I registered a new handler (a special dependency behaviour like "require" or "exports" ) called "delay" that provided a callback function for the module definition :

basically, this works this way :

define(["delay"], function ( delay ) {

    var Module = ... ;

    setTimeout(function(){ //Some asynchronous actions
        delay(Module) ; //This does actually returns the Module's value and triggers the "defined" event.
                        //It's actually transparent so the other modules just "feel" that the network's load time was a bit longer.
    },1000);

    return Module ; //This does not do anything because delay is active

});

Thanks to this asynchronous definition, I can now scan the directory (an asynchronous request) and then transparently load all the modules. The modifications represents only about 10 lines so if someone is interested, I post it there : https://github.com/jrburke/requirejs/pull/1078/files

Apparently it's not the first request for asynchronous exports but it's still in debate so I just use this for personal projects.

OTHER TIPS

It seems to me you could parcel off the functionality that records what file types are available into a new module, maybe named FileTypeRegistry. So your config files could be like this:

define(['FileTypeRegistry'], function ( FileTypeRegistry ) {

    var Interface = ... ;

    ...

    FileTypeRegistry.registerFileType("config1", Interface);    
});

These modules don't need to return a value. registerFileType just registers a type with the registry.

FileReader.js could contain:

define(["FileTypeRegistry", "config1", "config2", "config3"], function ( FileTypeRegistry ) {

    var FileReader = {};

    var FileReader.openFile = function (...) {
        var impl = FileTypeRegister.getFileTypeImplementation(...);
    };

    return FileReader;
});

getFileTypeImplementation retrieves an implementation (an Interface previously registered) on the basis of a type name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top