forkjoin
Version:
Fork/Join primitives for async programming (experimental)
293 lines (275 loc) • 7.25 kB
JavaScript
// Generated by CoffeeScript 1.9.1
(function() {
var async, collect, createTask, forEach, fork, forkjoin, head, isFunction, isFuture, join, lift, map, resolve, seq,
slice = [].slice;
isFunction = function(f) {
return 'function' === typeof f;
};
isFuture = function(a) {
if (a != null ? a.isFuture : void 0) {
return true;
} else {
return false;
}
};
head = function(a) {
if (a) {
return a[0];
} else {
return void 0;
}
};
async = function(f) {
return function() {
var args, error, go, j;
args = 2 <= arguments.length ? slice.call(arguments, 0, j = arguments.length - 1) : (j = 0, []), go = arguments[j++];
try {
return go(null, f.apply(null, args));
} catch (_error) {
error = _error;
return go(error);
}
};
};
fork = function(continuable, args) {
var _continuations, link, propagate, self;
if (args == null) {
args = [];
}
if (!isFunction(continuable)) {
throw new Error("Not a function.");
}
_continuations = [];
link = function(go) {
var continuation, found, j, len;
if (isFunction(go)) {
found = false;
for (j = 0, len = _continuations.length; j < len; j++) {
continuation = _continuations[j];
if (continuation === go) {
found = true;
}
}
if (!found) {
_continuations.push(go);
}
}
};
propagate = function() {
var go;
while (go = _continuations.shift()) {
if (self.rejected) {
go(self.error);
} else {
go(null, self.result);
}
}
};
self = function(go) {
return join([self], function(error, results) {
if (error) {
return go(error);
} else {
return go(null, head(results));
}
});
};
self.evaluate = function(go) {
link(go);
if (self.settled) {
return propagate();
} else if (self.evaluating) {
} else {
self.evaluating = true;
return join(args, function(error, args) {
if (error) {
self.error = error;
self.fulfilled = false;
self.rejected = true;
self.evaluating = false;
return propagate();
} else {
return continuable.apply(null, args.concat(function(error, result) {
if (error) {
self.error = error;
self.fulfilled = false;
self.rejected = true;
self.evaluating = false;
propagate();
} else {
self.result = result;
self.fulfilled = true;
self.rejected = false;
self.evaluating = false;
propagate();
}
self.settled = true;
return self.pending = false;
}));
}
});
}
};
self.method = continuable;
self.args = args;
self.fulfilled = false;
self.rejected = false;
self.settled = false;
self.pending = true;
self.evaluating = false;
self.isFuture = true;
return self;
};
join = function(args, go) {
var arg, i, j, len, resultCount, results, settled, tasks;
if (args.length === 0) {
return go(null, []);
}
tasks = [];
results = [];
for (i = j = 0, len = args.length; j < len; i = ++j) {
arg = args[i];
if (isFuture(arg)) {
tasks.push({
future: arg,
resultIndex: i
});
} else {
results[i] = arg;
}
}
if (tasks.length === 0) {
return go(null, results);
}
resultCount = 0;
settled = false;
tasks.forEach(function(task) {
return task.future.evaluate(function(error, result) {
if (settled) {
return;
}
if (error) {
settled = true;
go(error);
} else {
join([result], function(error, localResults) {
if (error) {
settled = true;
return go(error);
} else {
results[task.resultIndex] = head(localResults);
resultCount++;
if (resultCount === tasks.length) {
settled = true;
return go(null, results);
}
}
});
}
});
});
};
resolve = function() {
var args, go, j;
args = 2 <= arguments.length ? slice.call(arguments, 0, j = arguments.length - 1) : (j = 0, []), go = arguments[j++];
return join(args, function(error, results) {
return go.apply(null, [error].concat(results));
});
};
createTask = function(continuable) {
if (!isFunction(continuable)) {
throw new Error("Not a function.");
}
return function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return fork(continuable, args);
};
};
seq = function(_futures) {
return fork(function(go) {
var futures, next, results;
futures = _futures.slice(0);
results = [];
next = function() {
var future;
future = futures.shift();
if (future) {
future(function(error, result) {
if (error) {
return go(error);
} else {
results.push(result);
return next();
}
});
} else {
go(null, results);
}
};
next();
});
};
collect = function(futures) {
return fork(join, [futures]);
};
map = function(array, defer) {
var element;
return collect((function() {
var j, len, results1;
results1 = [];
for (j = 0, len = array.length; j < len; j++) {
element = array[j];
results1.push(defer(element));
}
return results1;
})());
};
forEach = function(array, defer) {
var element;
return seq((function() {
var j, len, results1;
results1 = [];
for (j = 0, len = array.length; j < len; j++) {
element = array[j];
results1.push(defer(element));
}
return results1;
})());
};
lift = function() {
var f, futures, j;
futures = 2 <= arguments.length ? slice.call(arguments, 0, j = arguments.length - 1) : (j = 0, []), f = arguments[j++];
return fork(function(go) {
return join(futures, function(error, results) {
if (error) {
return go(error);
} else {
return go(null, f.apply(null, results));
}
});
});
};
forkjoin = {
async: async,
task: createTask,
fork: function() {
var args, continuable;
continuable = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return fork(continuable, args);
},
join: join,
isFuture: isFuture,
resolve: resolve,
seq: seq,
collect: collect,
map: map,
forEach: forEach,
lift: lift
};
if (typeof window !== "undefined" && window !== null) {
window.forkjoin = forkjoin;
} else {
module.exports = forkjoin;
}
}).call(this);