function Async() { return { apply: apply, bind: bind, fail: fail, http: http, map: map, parallel: parallel, run: run, sequence: sequence, wait: wait, wrap: wrap }; function apply(f, x) { return function(g) { g(f(x)); }; } function bind() { var m, steps, i; if(arguments.length < 1) { return wrap(); } m = arguments[0]; steps = arguments; i = 1; return function(f) { var step = function(x) { if(i < steps.length) { steps[i++](x)(step); } else { return f(x); } } m(step); }; } function fail(message) { return function(f) { console.log(message); } } function map(mapper) { return function(x) { return function(f) { f(mapper(x)); }; }; } function parallel() { var threads = arguments; var pending = threads.length; var results = []; var returned = []; return function(f) { var useResult = function(i) { return function(x) { if(!returned[i]) { results[i] = x; returned[i] = true; pending--; if(pending < 1) { f(results); } } }; }; for(var i = 0; i < threads.length; i++) { threads[i](useResult(i)); } }; } function run() { var m; if(arguments.length == 1) { m = arguments[0]; } else { m = sequence.apply(null, arguments); } m(function() {}); } function sequence() { var steps = arguments; var i = 0; return function(f) { var step = function(x) { if(i < steps.length) { steps[i++](step); } else { f(x); } } step(); }; } function wait(delay) { return function(f) { setTimeout(f, delay); }; } function wrap(x) { return function(f) { f(x); }; } function http(query) { return function(f) { var xhr = new XMLHttpRequest(); xhr.addEventListener('load', function() { f(this); }); xhr.open(query.method, query.url); if(query.method == 'POST') { xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } xhr.send(query.body); }; } }