I couldn't think of a good title for this one, but here's what I needed to accomplish: To loop over an array, but not moving onto the next element until finished with the current one. This is simple in synchronous, blocking code, but once you get to the world of Node.js and non-blocking calls going on everywhere, it becomes wildly more difficult. One could add the word "Semaphore" to this post and it wouldn't be too off the wall.
Take the following code for example:
var array = ["www.google.com", "www.yahoo.com", "www.microsoft.com", "www.jasontconnell.com", "givit.me"];
array.forEach(function(d, i){
http.get({ host: d, port: 80, path: "/" }, function(res){
console.log("index " + i + " got the response for " + d);
});
});
Add in the appropriate "require" statements, run the code, and this is what I got the first time I ran it:
index = 0 - got response for www.google.com
index = 3 - got response for givit.me
index = 2 - got response for www.microsoft.com
index = 4 - got response for www.jasontconnell.com
index = 1 - got response for www.yahoo.com
That is not a predictable order! So how do we fix this? EventEmitter!! This took me a surprisingly small amount of time to figure out. Here's the code:
var http = require("http");
var EventEmitter = require("events").EventEmitter;
var sys = require("sys");
function SyncArray(array){ this.array = array };
require("util").inherits(SyncArray, EventEmitter)
SyncArray.prototype.forEach = function(callback, finishedCallback){
var self = this;
this.on("nextElement", function(index, callback, finishedCallback){
self.next(++index, callback, finishedCallback);
});
this.on("finished", function(){
});
self.next(0, callback, finishedCallback);
}
SyncArray.prototype.next = function(index, callback, finishedCallback){
var self = this;
var obj = index < self.array.length ? self.array[index] : null;
if (obj){
callback(obj, index, self.array, function(){
self.emit("nextElement", index, callback, finishedCallback);
});
}
else {
finishedCallback();
self.emit("finished");
}
}
var array = ["www.google.com","www.yahoo.com", "www.microsoft.com", "givit.me", "www.jasontconnell.com"];
var sync = new SyncArray(array);
sync.forEach(function(d, i, array, finishedOne){
http.get({ host: d, port: 80, path: "/" }, function(res){
console.log("index = " + i + " - got response from " + d );
finishedOne();
});
}, function(){
console.log("finished the sync array foreach loop");
});
And the output as we expected:
index = 0 - got response from www.google.com
index = 1 - got response from www.yahoo.com
index = 2 - got response from www.microsoft.com
index = 3 - got response from givit.me
index = 4 - got response from www.jasontconnell.com
finished the sync array foreach loop
Feel free to update this with best practices, better code, better way to do it in Node.js (perhaps built in?), etc, in the comments. I'm really excited about this since holy F#@$ that's been frustrating!