AJ,
This is good stuff, thanks! I'd love to see more like this. I've started using FuturesJS and so far haven't had too many problems. Sorry in advance for the large posting, but I would be interested in your insights.
The use case for subscriptions sounds like it might perfectly satisfy one of my needs. However, I feel that there must be other uses for it, but I'm having a hard time figuring out scenarios in my application. Perhaps if I give you some details, you will be able to easily suggest good uses for FuturesJS.
First of all, my ultimate goal is rather grandios, so I'll just do a quick overview of it. I'm attempting to build a component-based infrastructure on top of jquery to meet my needs. There are many MVC-based javascript frameworks out there, but I haven't seen anything purely component-based. In Java-land, I prefer the Wicket framework, and am attempting to see if something similar can be done in Javascript-land for single-page applications utilizing RESTful services and JSON data.
If someone reading this message does't know the difference between component-based and MVC frameworks, this post gives a decent overview:
The following is a rough initial stab at a design and may change drastically.
1. Components are self-contained objects that contain all of the logic required to render themselves.
2. A component can be inserted into any location in the DOM.
3. Multiple instances of a Component can be created and inserted into the DOM in multiple locations.
4. A component keeps a reference to a pre-compiled template used to render itself
5. A component has a Model that references data in a DataProvider object. The data in the Model is used to render the Component.
6. A DataProvider object knows how to retrieve data from a JSON-based service and/or local HTML5 storage, etc.
7. Components listen for changes in their data Model and automatically refresh the UI when the data changes (only the specific component who's data has changed, not the full page)
8. Multiple instances of a component, or even different types of components can reference the same data in a DataProvider. Thus a single property change will automatically update in all places it is displayed in the UI.
9. Components can contain other components, which can contain components, etc.
10. Components might get used inside of many other components, encouraging code reuse
The goal is to encapsulate logic into small, self-contained, testable components. Once components are created, an application built on this infrastructure should only need to perform CRUD actions on the DataProvider, and all server communication and UI updating will automatically be handled. This should significantly simplify event handler code.
If you understand what I've described above, what features of FuturesJS do you see me utilizing? Any specific suggestions you can provide? Currently, I've experimented with FuturesJS sequences, promises, and joins. For the most part, it seems to be working well.
SEQUENCES
--------------------
Initially, my single-page application loads from an HTML file with included javascript files. When the document is ready, I initiate a sequence to do the following:
1. Load a JSON document via AJAX that describes the current state of the application UI for the logged in user. For instance, what is the organization of their dashboard widgets, what tabs have they created, which tab is selected, etc. This describes which types of Components are used and where they are placed.
2. Based on the UI description just loaded, one or more container files are loaded via AJAX. A container file is a self contained file that contains both HTML template markup and javascript code to fully describe one or more related Components. All of the container files necessary to provide the components used in the UI are loaded.
3. Component instances are created and rendered based on the UI description.
A simplified version of the code looks like this:
Futures.sequence(function (callback) {
// Load UI description for user
App.loadUI(callback);
})
.then(function(callback, prevResult, index, results) {
// Load all containers specified in UI description
App.loadContainers(callback);
})
.then(function(callback, prevResult, index, results) {
// Build and render UI
});
Is this the proper way to use subscriptions? Anything you can recommend? I'm not really using return values at this time, instead keeping track of data in my App object, but may explore that more.
PROMISES and JOINS
---------------------------------
As an example, inside of App.loadContainers, I use both promises and joins. Simplified, it looks like this:
App.loadContainers = function(onLoaded) {
var promises = [];
// Loop through containerNames array (populated by loadUI)
$.each(containerNames, function(i,name) {
if (containers[name]) {
// Container already loaded
return;
}
promises[i] = Futures.promise();
var $container = $("<div>");
// Add this container to temporary markup cache
// until markup is compiled into template functions
containerMarkup.append($container);
// Load a container
$container.load(containerPath+"/"+name, function(response, status, xhr) {
if (status === "error") {
log.debug("Error loading container "+name);
// Return without exectuing onLoaded callback
return;
}
log.debug("Container " + name + " loaded successfully.");
// Add this container to the App's containers array
containers[name] = $container;
promises[i].fulfill();
});
});
Futures.join(promises)
.when(function(p_arr){
log.debug("ALL CONTAINERS LOADED");
if (typeof onLoaded === "function") {
onLoaded();
}
})
.fail(function(p_arr) {
log.debug("ERROR LOADING CONTAINERS");
});
Using an array of promises and a join in this manner seems to work well until one of the container files specified doesn't exist. The join.when never runs (which it shouldn't), but fail() never runs either. Is there a way to get fail() to run? How do I set a timeout?
Ideally, my application should continue to function, even if a container file didn't load. I'd like to retry the loading in case there was just a network error. If the file still doesn't load, the UI should still be built and rendered, but any Components that haven't been defined would have a default component used instead and would display an error icon and/or a message indicating that the component couldn't be located.
Am I properly using promises and joins here? Is there a better approach to this?
Thanks again, I'm excited to learn more about FuturesJS and utilize it in my projects.
Tauren