Handling Callbacks with a Depth-First Tree in JavaScript

One of the hardest things to do in JavaScript when working with complex data structures and a callback oriented platform is to know for sure when all of your callbacks have been fully executed. This issue came to light when working with a MongoDB datastore that was being used to store an infinitely-deep nested menu structure.

This menu structure could be visualized as being a tree. In order to get the all of the needed menu items from this tree, a depth-first traversal of the tree was determined to be the easiest to track. Since I typically try to write the least amount of code possible to solve a problem, I started out with a set of simple callbacks that worked perfectly as long as there was only one menu-item that had any children. As soon as I had multiple children to traverse, the timing of the execution of the final callback happened well before it should have as well as occurring again at the correct time, causing some unexpected results.

I am sure that there are many ways to solve this particular issue that have some elegant algorithmic roots, I am trying to use the simplest method possible. In fact, there is a library that was originally written for use with NodeJS the solves this exact issue. The async library allows you to use async.each(array, iterator, callback);, which will call the iterator function for each member of array, and the iterator function should handle any necessary processing, calling the callback function when complete. If any of the instances of iterator throw an error, the other instances are short-circuited. If they all run to completion, the callback function passed in will be called only when all have completed successfully, greatly simplifying the logic.

A simple example with Mongo and recursion would be:

function loadMenu(element, index, array, done) {
    menu.model.find().exec().then(function (menus) {
        //Do Stuff Here...
        async.each(menus, loadMenu, done);
    });
}

async.each(menu, loadMenu, done);

Related Posts

Jul 8, 2014
3 minutes

Always Use Automated Integration Testing

QA or Quality Assurance of a software project is often the area of software development that is most neglected. Typically developers avoid software testing like their lives depended on it. While a basic level of testing is required for a single scenario to validate that your code “works”, the level of testing that is required to ensure that all users have a good user experience across all targeted platforms is something that a developer seems to think is beneath them.

Jan 4, 2015
3 minutes

Top Job Applicants Never Stop Learning

From time to time, my job allows me to be a part of the hiring process for our technical positions. Unfortunately for some of the applicants, I repeatedly come away from these interviews amazed at the responses I get from pretty standard and basic technical questions related to Web Development.

Recently we were looking for a front-end web developer that was good at UX and design and proficient at HTML, CSS, and JavaScript. One of the things that we tend to ask everyone is to rate themselves on a scale of 1 to 10 as to how good they are with each technology. The majority of responses are in the 5-8 range with the corresponding answers to the questions about each techology falling about in the range you would expect. A couple of applicants were brave enough to rate themselves at a 9.5 out of 10 on HTML, CSS, and JavaScript, leading us to believe they were “exceptional applicants”.

Mar 23, 2015
2 minutes

Google Chrome Improves JavaScript Speeds Again

One of the old rules of optimizing website load times for all browsers was that the browser didn’t begin to parse the downloaded JavaScript until each file was downloaded. Starting with Chrome 41, Google has announced that this is no longer the case.

In this announcement, Google has said that new versions of Chrome will begin parsing JavaScript as it is downloaded to the browser, even before the particular file’s download is complete. However, in order to see this happen more quickly, you must utilize the async or deferred tags in the <script> tag. JavaScript that is loaded without async or deferred is still a completely blocking action that pauses all rendering while downloading and parsing the JavaScript files. According to Google, this can improve website load time by up to 10%.