Reading genetic data in VCF and Tabix formats using an asynchronous library I'm working with an open-source library for processing and parsing genetic data in VCF and Tabix formats. It contains functions and classes that make it easy to read an index file (a Tabix) and load corresponding data from a compressed VCF. 

It works asynchronously (unsurprisingly), however, and I'm not sure of the best way to structure my code. I'm looking to make multiple calls to the getRecords() method—since I need to retrieve multiple discontinuous segments of data; because this is asynchronous, it seems like I also have to instantiate a new file reading object (here known as readBinaryVCF but roughly analogous to HTML's FileReader). Both the file reading object's constructor and the getRecords() function use callbacks. 

I'm not really experienced in JavaScript or asynchronous design patterns; I'm hoping it doesn't have to be this convoluted. Other solutions that I tried, though, like recursing or making multiple calls  to getRecords() on the same object, were not as flexible or as robust—or just flat out didn't work. 

The code sample below is the first iteration I've gotten to work. That being said, please tear it apart. I suspect there're many improvements to be made. 

EDIT: I'll leave this code alone as my first draft, but please look at the minor changes I've made in an answer below. I'm still hoping for help, preferably on top of my revisions. 

var regions = [ 
    [10, 10000, 100000], 
    [12, 10000, 100000], 
    [14, 10000, 100000], 
    [18, 10000, 100000]
];


var d = [];
var readers = [];

for (var a = 0; a &lt; regions.length; a++) {
    d.push([]);
    readers.push(new readBinaryVCF(tabix, vcf, function(x) {x = x;})); 
    //Should I just initialize these to null instead? 
}

regions.forEach(function(item, i) {

    readers[i] = new readBinaryVCF(tabix, vcf, function(tabixReader) {

        rbvcf = readers[i];

        var index = 0; 
        for (key in tabixReader.idxContent.namehash) {
            if (key == regions[i][0]) {
                index = tabixReader.idxContent.namehash[key];
            }
        }

        rbvcf.getRecords(index, regions[i][1], regions[i][2], function(data) {
            d[i] = data; 

            if (i == regions.length - 1) {
                var finalText = "";

                for (var j = 0; j &lt; d.length; j++) {
                    for (var k = 0; k &lt; d[j].length; k++) {
                        finalText = finalText + d[j][k] + "&lt;br /&gt;";
                    }
                }

                $(".text").html(finalText);
            }
        });
    });
});
A few painfully obvious edits of my own: 

The frivolous for-loop:

for (var a = 0; a &lt; regions.length; a++) {
    d.push([]);
    readers.push(new readBinaryVCF(tabix, vcf, function(x) {x = x;})); 
}


Why, why did I think this was a good idea? It's entirely unnecessary. 

Omitted. 

Callback-waiting:

if (i == regions.length - 1) {

   var finalText = "";

   for (var j = 0; j &lt; d.length; j++) {
       for (var k = 0; k &lt; d[j].length; k++) {
           finalText = finalText + d[j][k] + "&lt;br /&gt;";
       }
   }

   $(".text").html(finalText);
}


I thought I could wait until the "last" callback had ended before parsing my data, not realizing that—because they're, again, callbacks—they won't finish in order. This answer inspired me to use a manual counter to determine when they've all finished. 

Revised code:

var d = []; 
var readers = []; 
var callbacksRemaining = regions.length; 

regions.forEach(function(item, i) {

    readers[i] = new readBinaryVCF(tabix, vcf, function(tabixReader) {

        reader = readers[i];

        var index = 0; 

        for (key in tabixReader.idxContent.namehash) {
            if (key == regions[i][0]) {
                index = tabixReader.idxContent.namehash[key];
            }
        }

        reader.getRecords(index, regions[i][1], regions[i][2], function(data) {; 

            d[i] = data; 
            callbacksRemaining--; 

            if (callbacksRemaining &lt;= 0) {
                processData(d);
            }
        });
    });
});


Summary:

I've made these few minor edits, but my code still isn't running as fast as I'd like. I'm sure there are many more changes I could and should make—I'm still hoping for advice!