Warning, rant ahead.I've been growing less fond of JavaScript as of late, because I'm starting to get the feeling that it's nigh impossible to make high quality software using it. Especially when it's combined with HTML and thrown into embedded equipment to be used as the primary programming language. Case in point: the set top boxes produced by Amino.
These STBs have an image of BusyBox Linux installed with [some video stuff] overlaid with an install of Opera 11. Inside Opera, the JavaScript environment has been extended to include API objects and methods for interfacing with the rest of the STB's systems, such as the video player, network, hard disks, DVR and so on. In theory, it's pretty great. You can build up a complete middleware software stack using HTML5 and JavaScript inside Opera. Theory is about where that ends, and pretty soon after you start working with it you fall down a gradual hill of pain and misery.
Fingers could be pointed in many places, but I'll start with the JavaScript API. There is an object at top level scope called PVR, which is used to manage the recording device. It has a method called GetAssetIdList, which is supposed to return, well, the list of asset IDs identifying recordings. Sounds easy. Let's take a look at the documentation:
string PVR.GetAssetIdList()
*description goes here*
Return Value - An object with the following properties:
count - The number of asset IDs in the list
Array - The assets
I was quite baffled by the return value being an object with properties called .count and .Array, but whatever. Nevermind that it's documented as returning a string earlier, that's an easy goof and I forgive them. What I don't forgive is that return object.
See, first of all, if there
are no asset IDs, it returns undefined. What? That's not documented
anywhere and completely screws up code that checks for .count or .Array. Whatever, it took me 2 hours to figure out that it was returning undefined instead of an object because debugging this thing is impossible. More on that later.
Next up was figuring out why .Array is not a member of the returned object. Some of you may already suspect what's really going on here, but I didn't yet. I tried looping through it like so:
result = PVR.GetAssetIdList();
for (var i = 0; i < result.Array.length; i++) {
// Stuff
}
Predictably that causes the interpreter to die silently, because .Array doesn't exist and there's no debug output to tell me that. Eventually I gave up on that and tried enumerating the properties of the returned object to see
what it had in it. Hey,
no properties are returned! It's an empty object. What? That's not documented anywhere either!
Cursing profusely in my head at this point for wasting hours on something stupid, I finally decided to try alerting result.count. Lo and behold, it has a value, and it appears to be correct. But... if I try:
for (var key in result) {
}
I get no keys. So... the keys can't be enumerated. .Array holds nothing still, and I can't enumerate what the name of the property really is!
By now any sensible person would have made the assumption I finally came to: the result is an array. I tried:
for (var i = 0; i < result.count; i++) {
blah = result[i];
}
And it works.
Let me restate that: they reimplemented JavaScript arrays, don't document this, and on top of it use
count instead of the ubiquitous
length property. I was mad enough to destroy anything fragile nearby when I realized this. Fortunately, nothing fragile was within arm's reach, so I wasn't escorted off the premises for property damage.
I could rant on JavaScript's decision to include undefined in the first place, or how trying to use an undeclared variable before assigning to it causes exceptions but you can do that with object properties just fine, but that's for another day. Instead, I'll just mention that I had to waste another hour trying to figure out why our templating library didn't work unless I prefixed all of the JavaScript replacement targets with full.object.sub_object.references instead of just the references I wanted. It was because of that stupid enumerable property thing again. What point is there to hiding properties this way if you can still access them? I really hate some of these parts of JavaScript.
Anyway, this was all 100x worse because of the development environment. We could do some basic stuff directly in our browsers (Chrome, not Opera, but I trusted the two were similar enough), but anything of substance required special code to account for the JavaScript API not being available in our browsers. Oh, and if anything went wrong on the STB, you had to guess why, comment out blocks of code and use custom alerts (built in alerts are disabled) to figure it out. I don't know of any way to attach a debugger to it to get console output, but there might be something. The emulator claims to support that but I never figured out how to use its debugging interface either. Doesn't help that the emulator doesn't match the real thing in some ways. For example, the real thing is painfully slow for reasons I haven't yet figured out.
And to top it off, we had to slap this together with duct tape, prayers and the sacrifice of virgin bottles of Mountain Dew because of the development deadline. It's buggy as heck and the code is a mess. I know you can do that in any language, but trying to do all of this stuff in a single, self contained web page feels like it encourages the worst development practices. There are global variables storing things like the channel and program data. I accidentally aliased that once and wasted forever trying to figure out why something was null in one place but not another. This is largely a consequence of JavaScript's awful class system. I honestly thought (and still think) that I could get it done faster and in less effort with purely procedural code. Except when it's time to add features, because you can't pollute the global namespace forever...