Created
January 19, 2012 13:51
-
-
Save espadrine/1640136 to your computer and use it in GitHub Desktop.
Array Processing in Event Loops
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var async = require('async'); | |
function differedFactorial(n /* Number */, cb /* Function */) { | |
if (n < 0) cb(new Error('Complex infinity')); | |
setTimeout(function() { | |
var result = 1; | |
for (; n > 1; n--) { | |
result = result * n; | |
} | |
cb(null, result); // No errors, result is given. | |
}, 150 + Math.abs(300 * Math.random())); | |
} | |
var a = [2, 4, 6, 9]; | |
async.map(a, differedFactorial, function(err, res) { | |
console.log(res); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The following function is a dummy example of | |
// a function that is run in the next event loop cycle. | |
// A non-dummy example would be, for instance, an array | |
// of file names (as Strings), for which we want the contents. | |
// In node.js, reading from a file is non-blocking; | |
// it waits for the next cycle. | |
function differedFactorial(n /* Number */, cb /* Function */) { | |
if (n < 0) cb(new Error('Complex infinity')); | |
setTimeout(function() { | |
var result = 1; | |
for (; n > 1; n--) { | |
result = result * n; | |
} | |
cb(result); | |
}, 300); | |
} | |
// We want to apply our fellow differedFactorial function. | |
// However, that function spans across multiple event loop cycles. | |
// As a result, the following function, which would work otherwise, | |
// does not return the expected result of [2, ...]. | |
// | |
// Just like with coroutines, functions that span across EL cycles | |
// are unnoticeably "impure" for we cannot tell how they behave from | |
// how they look. Only the documentation can say. | |
var a = [2, 4, 6, 9]; | |
console.log(a.map(function(e) { | |
var result; | |
differedFactorial(e, function(res) { | |
result = res; | |
}); | |
return result; | |
})); | |
// The only way out is to use this function. | |
// A major contract we make with the user is that he *must* | |
// run the callback `cb` for each element passed in `f`. | |
// Otherwise, the whole thing never yields a result. | |
Array.prototype.asyncMap = function(f /* Function */, cb /* Function */) { | |
var l = [], len = this.length; | |
for (var i = 0; i < len; i++) { | |
f(this[i], i, this, function(e) { | |
l.push(e); | |
if (l.length === len) { | |
cb(l); | |
} | |
}); | |
} | |
}; | |
// Notice how that's actually a map we reimplement in there? | |
// Let's make use of the version of `map` that doesn't return anything. | |
Array.prototype.asyncMap = function(f /* Function */, cb /* Function */) { | |
var l = [], len = this.length; | |
this.forEach(function(v, i, a) { | |
f(v, i, a, function(e) { | |
l.push(e); | |
if (l.length === len) { | |
cb(l); | |
} | |
}); | |
}); | |
}; | |
a.asyncMap(function(e, i /* Number */, a /* Array */, cb /* Function */) { | |
differedFactorial(e, function(res) { cb(res); }); | |
}, function(result) { | |
console.log(result); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This time, the dummy non-blocking next-cycle function | |
// probably won't return values in order. | |
// Indeed, it runs the callback after a random amount of time. | |
function differedFactorial(n /* Number */, cb /* Function */) { | |
if (n < 0) cb(new Error('Complex infinity')); | |
setTimeout(function() { | |
var result = 1; | |
for (; n > 1; n--) { | |
result = result * n; | |
} | |
cb(null, result); // No errors, result is given. | |
}, 150 + Math.abs(300 * Math.random())); | |
} | |
// The old implementation doesn't care about the order of what | |
// was given in the array. The implementation is simpler, but | |
// it doesn't act like `map`. | |
Array.prototype.asyncMap = function(f /* Function */, cb /* Function */) { | |
var l = [], len = this.length; | |
for (var i = 0; i < len; i++) { | |
f(this[i], i, this, function(e) { | |
l.push(e); | |
if (l.length === len) { | |
cb(l); | |
} | |
}); | |
} | |
}; | |
var a = [2, 4, 6, 9]; | |
a.asyncMap(function(e, i /* Number */, a /* Array */, cb /* Function */) { | |
differedFactorial(e, function(res) { cb(res); }); | |
}, function(result) { | |
console.log('asyncMap: %s', result); | |
}); | |
// Again, the only way out is to use this function. | |
// A major contract we make with the user is that he *must* | |
// run the callback `cb` for each element passed in `f`, and | |
// he *must* give it the processed element | |
// *and* the associated index. | |
// Otherwise, the whole thing never yields a result. | |
Array.prototype.asyncOrderedMap = function(f /* Function */, | |
cb /* Function */) { | |
var processing = 0, | |
l = new Array(this.length), | |
len = this.length; | |
for (var i = 0; i < len; i++) { | |
f(this[i], i, this, function(e, idx) { | |
l[idx] = e; | |
processing++; | |
if (processing === len) { | |
cb(l); | |
} | |
}); | |
} | |
}; | |
a.asyncOrderedMap(function(e, | |
i /* Number */, | |
a /* Array */, | |
cb /* Function */) { | |
// The callback has one more argument, the index. | |
differedFactorial(e, function(res) { cb(res, i); }); | |
}, function(result) { | |
console.log('asyncOrderedMap: %s', result); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment