My Synchronous Javascript Solution
I wrote about the problem I was having with the asynchronous (albeit single-threaded) nature of Javascript in my last post.
After looking at the options to make my code more clear when using asynchronous calls in Javascript, I’ve decided to take a simple approach. Instead of using one of the libraries, I’ve come up with what is basically a different way to format my code.
I can’t tell if this will be my long-term answer yet; I’ll just have to get further into the project and see how it goes.
So, here’s what I did. First, I wrote a function to do the actual asynchronous call. The only special thing about it is that it returns TRUE or FALSE, plus the return object.
PX.Rest = function (method, url, data, callback) {
var successHandler, failureHandler, makeRequest, request;
successHandler = function (o) {
callback(true, o);
};
failureHandler = function (o) {
callback(false, o);
};
makeRequest = function () {
YAHOO.util.Connect.initHeader('Content-Type', 'text/json', true);
request = YAHOO.util.Connect.asyncRequest(
method,
url,
{ success: successHandler,
failure: failureHandler,
connTimeout: 8000
},
data
);
};
makeRequest();
};
Now, this lets me write my code like so:
// The first AJAX call.
PX.Rest(method, sUrl, jsonData,
function (result, o) {
if (result) {
// If I'm here, it means the initial call was successful.
// The second AJAX call (for example, to tag the record I just created).
PX.Rest(method, sUrl, undefined,
function (result, o) {
if (result) {
// The second call passed also
} else {
// The second call failed (after the first call succeeded)
}
// Code that runs no matter if the second call passes or fails
}
);
// Code that runs when the first call passes but BEFORE the second one returns.
} else {
// The first call failed.
// In this example, I don't do anything else.
}
}
);
Now it flows! It’s almost like using nested if statements.
Here’s why I (think) it works:
- The arguments are ordered in such a way that the last one is the callback. This allows you to simply indent it in order to make it obvious you are inside conditional code.
- The PX.Rest function from above returns TRUE or FALSE, which allows me to use a ‘If (request)’ in-line. That way, I can continue to simply indent and don’t have to create separate callbacks, define them ahead of time, etc.
I certainly don’t think this is a perfect solution, but it does allow me follow the flow of my code much more easily. I wouldn’t want to go more than two or three calls deep, though!
Wrapping my head around Asynchronous Javascript
Most of the Javascript I use involves AJAX. Even before I really knew how to program in Javascript , my first cut & paste programs were AJAX simply because of the site I’m building. As I get deeper into the coding, trying to get multiple parts of my site to work together, the asyncronous model is getting harder and harder to work with.
Here is a simple pseudo-code example:
myFunction = function () {
successHandler = function () {
}
failureHandler = function () {
}
ajaxSetup = function () {
}
ajaxSetup();
}
That’s not too bad. As I’ve said before, I think it’s a bit verbose, but workable.
Putting it into the regular flow of code is harder. Here is my (made up) example:
- Create a new window
- Get data from the server
- Format the data with links
- Update a counter on the server
In pesudo-code again, it might look like this:
createMyWindow();
myFunction = function () {
successHandler = function () {
formatTheData();
myCounterUpdate = function () {
myCounterUpdateSuccessHandler = function () {
}
myCounterUpdateFailureHandler = function () {
}
myCounterUpdateajaxSetup = function () {
}
myCounterUpdateajaxSetup();
}
}
failureHandler = function () {
}
ajaxSetup = function () {
}
ajaxSetup();
}
Yuck! I actually have some code like this and was just going to deal with it. But, one of my flows required a third layer.
I simply can’t maintain any sense of the program with so many layers! So, I went looking for solutions and found this blog post by Harry Fuecks. It’s a couple of years old, but seems to capture exactly what my issue is and has some solutions. One is called Narrative Javascript and another is jwacs.
I’m not sure I like any of the solutions, though, since they involve adding a lot more code. But, I’m still wrestling with this issue, trying to re-factor a bit to gain control and see what other solutions there might be.
“Dime a Dozen”
I’m searching for a co-founder. It’s not easy. I’ve been attending group meetings and events for months where the kind of person I’m looking for might attend, but haven’t had any luck (yet).
My latest idea was to simply post an ad. I’ve had great success selling, giving away and buying things on Craigslist, so it seemed like a natural choice.
I think of Craigslist as free, but quickly remembered that they do charge for some things and job postings in the San Francisco Bay Area are one of them. I didn’t really want to spend $75, so I choose the Gigs/Computers area instead of Jobs. So far, so good.
I then wrote what I thought was a very clear and simple add. I clicked “No Pay” at the bottom (no bait and switch here!). After I posted, I quickly received a response for a company who was selling the services that I menteioned in my ad. So far, so good. And, fast!
Then– wham! Flagged.
Flagged?! Why?
I re-read the Terms of Service and was sure I was fine. So, I posted my query to the discussion board suggested in the message and found out right away what the issue was: people want to get paid. And, even though my ad was not against the Terms of Service, they hate “no pay” ads.
- A dime a dozen.
- Everyone wants a free geek (hey! I’m the geek here!).
- People just want to pay their rent.
- No Pay – No Stay.
Here’s the actual ad, in case YOU are interested.
I’m building a web tool for software test design (QA, basically) and need someone who can make it look good. I have ideas in terms of the functionality and some vague ideas about how it will look, but have zero ability to actually make it gel in terms of design.
Since I have the technical skills, I’m not looking for a framework or a turnkey site. I’m specifically looking for someone who can create a look and feel, including colors, typography, graphics, etc. and then put it all together using CSS, HTML, graphics and fonts. I’ve started using Yahoo’s YUI, so experience or a desire to learn how it works is necessary. Knowledge and expertise in user experience would also be great.
My goal is to get this tool working to the point where I can give a demonstration, then start looking for funding. In the meantime, I’m out of a job and working on this full-time.
So, are you interested in being a co-founder? Having some kind of deferred compensation? Out of work and bored and want something to add to your portfolio? I’m open to pretty much anything, so if you are the person that I’ve described above, let’s talk!
Managing a non-trivial amount of JavaScript
Even though I’m still new to JavaScript, I’m using it most of the time now. My initial attempts with copy & pasting code, watching YouTube videos, reading blogs, etc. were not getting me up to speed fast enough. I also came to realize that I had some fundamental misunderstanding about the language. So, I took a break and read Douglas Crockford’s JavaScritpt: The Good Parts. How refreshing!
Now many of the issues I was having, especially when comparing Perl code with JavaScript, had real answers. And, getting some real answers for differences in style of JavaScript code had some actual answers as well (due to things like the automatic semi-colon insertion).
My biggest discovery of all is the “Method Pattern.” (pp. 40-21). Eric Miraglia explains it very, very well in his blog post. This shows how to avoid Global Variable problems, make code more modular and all that other good stuff. It is *very* verbose (in my opinion). But, it works, I understand it and I can build a site with code structured like this with confidence.
With this new knowledge and the knowledge I already had about page size and performance issues with Rich Internet Applications (RIAs), I developed a way to load code on demand. I first wrote a “Loader” script. Then, I wrote a template to use that would interact with the Loader.
An example. Let’s say I wanted to open an interactive window. This window might require CSS files, JavaScript files, markup and data. And, some of it might already be loaded, but there is no guarantee. So, instead of dealing with this when you want to make the call, just call the Loader script with the action you want and let it take care of it.
Examples:
To open a dialog box:
pxLoader.open("dialog_box");
To close the dialog box:
pxLoader.close("dialog_box");
To make this work, I would need a standard set of commands (init, open, close, etc.)
So, if you want to check it out, here is the actual code. I ran it through JSLint with Recommended Options and Good Parts turned on. Be aware that I’ve only used it a bit and so have not flushed out all the issues that will make this truly robust.
Note: px is just a placeholder name. “plain_jane” would become “dialog_box” or “category_dialog) or whatever feature you were implementing. Oh, and I log. A lot.
// Copyright (c) 2009 Brian Estes - All Rights Reserved
/*global YAHOO */
YAHOO.namespace("px.plain_jane");
YAHOO.px.plain_jane = function () {
YAHOO.log("Instantiating", "begin", "pxPlainJane");
var example, setupListeners;
example = function (o) {
YAHOO.log("examle", "begin", "pxPlainJane");
YAHOO.log("example", "end", "pxPlainJane");
};
setupListeners = function () {
YAHOO.log("setupListeners", "begin", "pxPlainJane");
YAHOO.util.Event.addListener(
"plain_jane_one",
"click",
function () {
YAHOO.log("clicked plain_jane_update", "info", "pxPlainJane");
}
);
YAHOO.util.Event.addListener(
"plain_jane_two",
"click",
function () {
YAHOO.log("clicked plain_jane_one", "info", "pxPlainJane");
YAHOO.px.Loader.open('some_package');
}
);
YAHOO.log("setupListeners", "end", "pxPlainJane");
};
return { // public methods
init: function (o) {
YAHOO.log(".init", "begin", "pxPlainJane");
example(o);
setupListners();
YAHOO.log(".init", "end", "pxPlainJane");
},
open: function (o) {
YAHOO.log(".open", "info", "pxPlainJane");
},
refresh: function (o) {
YAHOO.log(".refresh", "info", "pxPlainJane");
},
close: function (o) {
YAHOO.log(".close", "info", "pxPlainJane");
},
destroy: function (o) {
YAHOO.log(".destroy", "info", "pxPlainJane");
},
exists: function (o) {
YAHOO.log(".exists", "info", "pxPlainJane");
},
visible: function (o) {
YAHOO.log(".visible", "info", "pxPlainJane");
},
ready: function (o) {
YAHOO.log(".ready", "info", "pxPlainJane");
}
};
}();
YAHOO.util.Event.onDOMReady(function () {
//YAHOO.px.plain_jane.init();
});
YAHOO.log("Instantiating", "end", "pxPlainJane");
Image sprites
Sprites were easier than I expected. Make a graphic, put multiple icons inside, put the ‘hover’ or rollover versions in there as well, then refer to them by coordinates.
The next step is to use this technique to make the site look good.
For that, I need someone who can design.
Make and Combine
The build script is working pretty well now. I decided to split the build and the combine scripts into two. It turns out that they were essentially two sets of code anyway and the only thing they had in common was that you run combine after make most of the time.
There are still issues knowing which scripts have loaded. If I include a script inside a combo file, YUI’s Get (or the browser) doesn’t know if it is about to pull in a duplicate set of code. Since I have the list of files inside the combined file, I can check to see if each file is loaded before passing it off to Get.
I’m also a bit concerned that I have integrated this too deeply. To have server side and client side scripts both using the same configuration file is great at preventing you from forgetting to make changes in two places. But, the implications of a change need to be considered across two different code-bases.
Integration
Everything is working today, although I would not consider this script done. It will minify and compress all of my JavaScript and CSS files, compile the Jemplates and then build concatenated versions of any files I specify. It is smart enough to know that files that end in -min or -combo are “object” files and not to delete them.
I’ve also move the configuration for the package (“combo”) management system into a separate file that both the build script and the core JavaScript file pull from.
The issues I have now: how to deal with duplicate copies of the same code if it is in regular, -min or -combo. I have a list of all the files in the combo files, but there is still a chance to duplicate one if it is the “debug” version and the other is not.
Also, I haven’t coded the part the knows to skip files that are already loaded as part of a combo. I’m using YAHOO’s Get feature to download files (and it is smart enough to know if a file has already been downloaded), but I’m having to duplicate some of this functionality myself, which is not a good thing.
This is taking much longer than I want. But, seeing a single file that contains all of my current JavaScript & templates load which is smaller than just ONE of the files I had before does show that this is really going to make my site much faster.
Next up, image sprites.
Again with the combo files
With a trip to San Francisco and a Walk-a-Thon at my kids school today, it is really cutting into my productivity. Or, at least it’s a good excuse; I can’t tell which.
I looked more carefully at YUI Builder today and the ant-driven procedure they use to build YUI itself. I probably really should be using something like this, but since I don’t totally understand it, I’m reluctant.
I did spent quite a bit of time first refining my build script and then adding the combo file functionality. It actually works right now, but I’m going to take another look at it after a little brake to make sure it is doing exactly what I want.
Because I’ve forged a deep relationship between my build and my package (“combo”) management system, there are more cases to consider.
Combo files
After doing more refinement on the build script, I started implementing the combined files. Getting the format and the capabilities just right is taking me a while.
For a few examples, should I be able to mix JavaScript an CSS? How about JavaScript and Jemplates? Should I specify a “package” to include in the combined file or choose the files individually?
Hopefully I’ll have more of this worked out tomorrow.
Build script
I’m deep in the middle of writing my build script. I looked at many of the standard tools like GNU’s make, Boost.Jam, etc., but settled on simply writing my own script.
As usual, it started out easy. But, got tough quickly. Not so much in terms of the code, but making decisions where I want all of these files, how flexible I want the system to be and decisions like that.
So far, I have a working build script that can do what I want except for the combo files. It will:
- compress/minify (with YUICompressor)
- remove YAHOO.log statements (I’m surprised this isn’t automatic)
- compile (Jemplate)
- copy to a new directory (if desired)
- add -min to the name
- add a new extension (if desired)
- keep a SHA1 digest on the source files
- “rebuild” only when something changes
- show all “object” files it knows about
- show all “source” files it knows about
- clean (i.e. delete all the object files)
- work on JS, CSS and Template Toolkit files
I’m afraid adding the combo option is going to be about as hard as what I’ve done so far. But, we’ll see. I might even post the code if anyone is interested.
Leave a Comment
Leave a Comment
Leave a Comment