Permalink
Browse files

Merge branch 'refs/heads/develop'

  • Loading branch information...
2 parents 82e4bee + 09d2904 commit c7c048cd48acff63695ee0c2e57df6e1f4cd880a @kilianc committed Nov 18, 2011
Showing with 174 additions and 139 deletions.
  1. +47 −24 README.md
  2. +30 −39 lib/fnqueue.js
  3. +2 −2 package.json
  4. +95 −74 test/fnqueue-test.js
View
@@ -2,65 +2,69 @@
A powerful utility for function chaining (inspired by [async](https://github.com/caolan/async)).
-## Dependencies
+## Engine
-- nodejs v0.4.12+
-
-## Installation as submodule
-
- $ git clone git://github.com/kilian/node-fnqueue.git
+- nodejs v0.4.12+ (tested with v0.6.x)
## Installation with npm
$ npm install fnqueue
-## Usage
+## Syntax
-Parameters:
+```javascript
+new FnQueue(functionsList[, callback, concurrencyLevel]);
+```
+##Parameters
-- Object of functions
-- Global callback
-- Concurrency level (default is max possible)
+1. `functionsList` __(Object)__ a list of Functions. Each function can declare implicit dependencies as arguments and assume you provide a single callback as the last argument.
+2. `callback` __(Function(err, data))__ the complete callback in the conventional form of `function (err, data){ ... }`
+3. `concurrencyLevel` __(Number/String: defaults to 'auto')__ the concurrency level of the chain execution, can be `'auto'` or `N* = { 1, 2, ... }`
-Each field of first parameter (Functions object) must be an Array / Function.
+##Notes
-If you pass an Array you are declaring a needed dependency with another function, otherwise you are passing a Function, then you are declaring a dependency less step.
+FnQueue runs a list of functions, each passing their results to the dependent function in the list. However, if any of the functions pass an error to the callback, the next function is not executed and the main callback is immediately called with the error.
-Each function with a dependency is called with the result of the depended function as parameter.
+Each dependency/argument must be named with the label of the dependent function in the `functionsList` (the first constructor argument).
+Each function with a dependency will be called with the result of the dependent function as expected. __(YES this is a fucking cool introspection!)__
-The global callback is called on the first error, or at the end of all functions. The first parameter is the err (if provided) and the second one is a data object with the result of the chain.
+The global callback is called once, on the first error or at the end of the execution. A data object will be provided with the indexed result of the functions.
FnQueue magically resolves all dependencies and executes functions in the right order with the provided concurrency level.
```javascript
-var FnQueue = require('node-fnqueue');
+var FnQueue = require('fnqueue');
```
or for a verbose mode:
```javascript
-var FnQueue = require('node-fnqueue').verbose();
+var FnQueue = require('fnqueue').verbose();
```
Example:
```javascript
new FnQueue({
- funnyStuff: ['processSomething', 'searchSomething', function(time, searchResults, callback){
+ // this will wait for 'processSomething' and 'searchSomething' and will be called with the respective results
+ funnyStuff: function(processSomething, searchSomething, callback){
// do something silly
callback(null, 'ciao!');
- }],
+ },
+ // this will be called instantly
searchSomething: function(callback){
// do something with database
callback(err, results);
},
- update: ['searchSomething', function(searchResults, callback){
+ // this will wait 'searchSomething'
+ update: function(searchSomething, callback){
// change values inside results and save to db
callback(err); // no needs to return values
- }],
- processSomething: ['searchSomething', function(searchResults, callback){
+ },
+ // this will wait 'searchSomething'
+ processSomething: function(searchSomething, callback){
var start = new Date().getTime();
- // write a file log
+ // do something slow
var elapsedTime = new Date().getTime() - start;
- callback(err, elapsedTime); // logs write time;
+ callback(err, elapsedTime);
}]
},function(err, data){
@@ -73,6 +77,25 @@ new FnQueue({
console.log(data.funnyStuff); // 'ciao!'
}, 1);
```
+
+##Introspection profiling results
+
+Profiling results are pretty good, Function.toString() took up __~2 seconds__ every __1 Million__ of executions.
+
+ Lines of code time (ms) Platform
+ ---------------------------------------------------------------------------------------------------
+ 800 1808ms OSX Lion 2.2 GHz Intel Core i7 / nodejs v6.0.1
+
+## Test
+
+Tests depends on http://vowsjs.org/ then
+
+ npm install -g vows
+ npm install
+ npm test
+
+![tests](http://cl.ly/1R2q3h0G2c3a41303R1I/fnqueue_test_v2.png)
+
## License
_This software is released under the MIT license cited below_.
View
@@ -1,4 +1,4 @@
-function FnQueue(tasks, callback, concurrecyLevel, stop){
+function FnQueue(tasks, callback, concurrecyLevel, stop) {
this.tasks = tasks;
this.callback = callback;
this.callSequence = [];
@@ -7,23 +7,23 @@ function FnQueue(tasks, callback, concurrecyLevel, stop){
this.concurrecyLevel = concurrecyLevel || 'auto';
this.runningNb = 0;
!stop && this.callNextFunction();
-};
+}
-FnQueue.verbose = function(){
+FnQueue.verbose = function () {
FnQueue.prototype.isVerbose = true;
return FnQueue;
};
FnQueue.prototype.isVerbose = false;
-FnQueue.prototype.onTaskComplete = function(taskName, err, result){
+FnQueue.prototype.onTaskComplete = function (taskName, err, result) {
- if(this.gotError)
+ if (this.gotError)
return;
this.runningNb--;
- if(err){
+ if (err) {
this.gotError = true;
this.callback && this.callback(err, this.getResults());
return;
@@ -33,95 +33,86 @@ FnQueue.prototype.onTaskComplete = function(taskName, err, result){
this.callNextFunction();
};
-FnQueue.prototype.getResults = function(){
+FnQueue.prototype.getResults = function () {
var results = {};
for(var taskName in this.results)
results[taskName] = this.results[taskName].result;
return results;
};
-FnQueue.prototype.callNextFunction = function(){
+FnQueue.prototype.callNextFunction = function () {
var nextFunction, dependencies, finish = true, args;
- for(var taskName in this.tasks){
-
- if(this.concurrecyLevel != 'auto' && this.runningNb >= this.concurrecyLevel)
- return;
+ for(var taskName in this.tasks) {
- dependencies = this.tasks[taskName];
finish = false;
- // no dependecies declared then we are passing the next function
- if(dependencies instanceof Function){
-
- nextFunction = dependencies;
- this.isVerbose && console.log('FnQueue executing: ' + taskName);
-
- this.runningNb++;
- delete this.tasks[taskName];
- this.callSequence.push(taskName);
+ if (this.concurrecyLevel != 'auto' && this.runningNb >= this.concurrecyLevel)
+ return;
- // postpone next function to next tick,
- // will allow to close the current stack and keep the call order.
- process.nextTick(nextFunction.bind(this, this.onTaskComplete.bind(this, taskName)));
+ nextFunction = this.tasks[taskName];
- continue;
+ if (nextFunction.length === 0) {
+ this.onTaskComplete(null, new Error('Invalid Function in chain: missing callback parameter'));
+ return;
}
+ dependencies = this.getFunctionArguments(nextFunction).slice(0, -1);
args = this.getDependencies(dependencies);
- if(args){
-
+ if (args) {
args.push(this.onTaskComplete.bind(this, taskName));
- this.isVerbose && console.log('FnQueue executing: ' + taskName);
+ this.isVerbose && console.log(' - FnQueue: executing: ' + taskName + '(' + dependencies.join(',') + ') { ... }');
this.runningNb++;
delete this.tasks[taskName];
this.callSequence.push(taskName);
- nextFunction = dependencies.pop();
nextFunction.apply(null, args);
- if(this.concurrecyLevel != 'auto' && this.runningNb >= this.concurrecyLevel)
+ if (this.concurrecyLevel != 'auto' && this.runningNb >= this.concurrecyLevel)
return;
}
}
- if(!finish && !nextFunction && !this.runningNb){
- dependencies.pop();
+ if (!finish && this.runningNb === 0) {
this.onTaskComplete(null, new Error('Unresolvable dependencies: function "' + taskName + '" requires [' + dependencies.join(',') + ']'));
return;
}
- if(finish && !this.runningNb && this.callback){
+ if (finish && !this.runningNb && this.callback) {
this.callback(null, this.getResults());
this.destroy();
}
};
FnQueue.prototype.start = FnQueue.prototype.callNextFunction;
-FnQueue.prototype.getDependencies = function(dependencies){
+FnQueue.prototype.getFunctionArguments = function (fn) {
+ return (/^function.+\(([a-z0-9\n\r\t ,]*)\)/i).exec(fn.toString())[1].trim().split(/[ ,\n\r\t]+/);
+};
+
+FnQueue.prototype.getDependencies = function (dependencies) {
var args = [];
- for(var i = 0; i < dependencies.length-1; i++){
+ for(var i = 0; i < dependencies.length; i++) {
- if(this.results[dependencies[i]] === undefined){
+ if (this.results[dependencies[i]] === undefined) {
return false;
}
var arg = this.results[dependencies[i]];
- if(arg !== undefined)
+ if (arg !== undefined)
args.push(arg.result);
}
return args;
};
-FnQueue.prototype.destroy = function(){
+FnQueue.prototype.destroy = function () {
delete this.results;
delete this.callback;
delete this.tasks;
View
@@ -3,7 +3,7 @@
"name": "fnqueue",
"main" : "./lib/fnqueue.js",
"description": "A powerful utility for function chaining",
- "version": "1.0.5",
+ "version": "2.0.0",
"engines": {
"node": ">= v0.4.7"
},
@@ -15,6 +15,6 @@
"url": "http://github.com/kilianc/node-fnqueue.git"
},
"devDependencies": {
- "vows": "0.5.x >=0.5.10"
+ "vows": "0.5.x >= 0.5.13"
}
}
Oops, something went wrong.

0 comments on commit c7c048c

Please sign in to comment.