Comparing version 0.0.1 to 0.0.3
71
index.js
@@ -5,12 +5,48 @@ module.exports = chainit; | ||
var Queue = require('queue'); | ||
var q = new Queue({ | ||
timeout: 0, | ||
concurrency: 1 | ||
}); | ||
var curIdx = 0; | ||
var queues = []; | ||
var currentDepth = 0; | ||
function pushTo(depth, task) { | ||
var queue = queues[depth] || (queues[depth] = getNewQueue(depth)); | ||
queue.push(task); | ||
} | ||
function getNewQueue(newDepth) { | ||
var queue = new Queue({ | ||
timeout: 0, | ||
concurrency: 1 | ||
}); | ||
queue.on('drain', function() { | ||
if (newDepth > 0) { | ||
wakeupChain(newDepth); | ||
} | ||
if (!queues.slice(newDepth).some(hasPending)) { | ||
currentDepth = newDepth; | ||
} | ||
}); | ||
function wakeupChain(depth) { | ||
if (!queues[depth + 1] || | ||
!queues.slice(depth).some(hasPending)) { | ||
queues[depth - 1].concurrency = 1; | ||
queues[depth - 1].process(); | ||
} | ||
if (depth > 1) { | ||
wakeupChain(depth - 1); | ||
} else { | ||
if (!queues.some(hasPending)) { | ||
depth = 0; | ||
} | ||
} | ||
} | ||
return queue; | ||
} | ||
var methods = Object.keys(Constructor.prototype); | ||
methods.forEach(function(name) { | ||
var original = Constructor.prototype[name]; | ||
var fName = Constructor.prototype[name].name || name; | ||
@@ -25,8 +61,19 @@ var chained = function() { | ||
var ldepth = currentDepth; | ||
if (currentDepth > 0 && queues[currentDepth - 1].concurrency > 0) { | ||
queues[currentDepth - 1].concurrency = 0; | ||
} | ||
var task = function(cb) { | ||
currentDepth = ldepth + 1; | ||
args.push(function() { | ||
curIdx = q.indexOf(task); | ||
var cbArgs = arguments; | ||
if (customCb) { | ||
customCb.apply(ctx, arguments); | ||
customCb.apply(ctx, cbArgs); | ||
} | ||
cb(); | ||
@@ -38,5 +85,3 @@ }); | ||
q.splice(curIdx + 1, 0, task); | ||
curIdx += 1; | ||
pushTo(currentDepth, task); | ||
return this; | ||
@@ -49,2 +94,6 @@ } | ||
return Constructor; | ||
} | ||
function hasPending(queue) { | ||
return queue.length >= 1; | ||
} |
{ | ||
"name": "chainit", | ||
"version": "0.0.1", | ||
"version": "0.0.3", | ||
"description": "Turn an asynchronous JavaScript api into an asynchronous chainable JavaScript api.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -6,10 +6,2 @@ # chainit | ||
Features: | ||
* supports async apis | ||
* supports nested calls | ||
* preserve nested calls order | ||
* preserve context in callbacks | ||
* preserve callback arguments | ||
## usage | ||
@@ -34,17 +26,23 @@ | ||
obj | ||
.method1() // 1 | ||
.method2() // 2 | ||
.method1(function(/* args */) { // 3 | ||
this.method1(); // 4 | ||
.method1() // 1st call | ||
.method2() // 2nd call | ||
.method1(function(/* args */) { // 3rd call | ||
this.method1(); // 4th call | ||
}) | ||
.method2(); // 5 | ||
.method2(); // 5th call | ||
``` | ||
Call order: | ||
1. method1 | ||
2. method2 | ||
3. method1 | ||
4. method1 | ||
5. method2 | ||
## features | ||
Features: | ||
* supports async apis | ||
* supports (crazy) nested calls | ||
* preserve nested calls order | ||
* preserve context in cb() | ||
* preserve cb(args) | ||
* supports process.nextTick(cb) | ||
* supports setTimeout(cb) | ||
* fully tested! `npm test` | ||
## examples | ||
@@ -51,0 +49,0 @@ |
@@ -9,3 +9,3 @@ describe('chaining an Api', function() { | ||
beforeEach(function() { | ||
o = new ChainApi; | ||
o = new ChainApi(); | ||
}); | ||
@@ -103,4 +103,81 @@ | ||
}) | ||
}); | ||
it('supports methods calling methods', function(done) { | ||
o | ||
.tripleConcat('one', function() { | ||
this.tripleConcat('two'); | ||
}) | ||
.tripleConcat('four', function() { | ||
assert.equal(o.s, 'one1-one2-one3-two1-two2-two3-four1-four2-four3-'); | ||
done(); | ||
}); | ||
}); | ||
it('supports nested and subsequent calls', function(done) { | ||
o | ||
.concat('s') | ||
.call(function() { | ||
o | ||
.concat('a', function() { | ||
o.concat('l') | ||
.call(function() { | ||
o.concat('u', function() { | ||
o.call(function() { | ||
o.call(function() { | ||
o.concat('t') | ||
}) | ||
}) | ||
}) | ||
.concat(' ') | ||
}) | ||
.concat('ç') | ||
}) | ||
.concat('a') | ||
}) | ||
.concat('', function() { | ||
o | ||
.call(function() {}) | ||
.concat(' v', function() { | ||
o.call(function() { | ||
o.concat('a'); | ||
}).concat(' ?').concat('?', function() { | ||
assert.equal(o.s, 'salut ça va ??'); | ||
done(); | ||
}) | ||
}) | ||
}) | ||
}); | ||
it('support crazy own chains', function(done) { | ||
o.multiConcat('multi', function() { | ||
assert.equal(o.s, 'a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-') | ||
done(); | ||
}) | ||
}) | ||
it('supports outside nextTick', function(done) { | ||
o.concat('one'); | ||
process.nextTick(function() { | ||
o.concat('two').concat('three', function() { | ||
assert.equal(this.s, 'onetwothree') | ||
done(); | ||
}) | ||
}) | ||
}) | ||
it('supports functions', function(done) { | ||
o.concat('one', named); | ||
function named() { | ||
o | ||
.tripleConcat('two') | ||
.tripleConcat('three', function() { | ||
assert.equal(this.s, 'onetwo1-two2-two3-three1-three2-three3-') | ||
done(); | ||
}) | ||
} | ||
}) | ||
}); |
@@ -8,12 +8,55 @@ module.exports = Api; | ||
Api.prototype.concat = function concat(sub, cb) { | ||
process.nextTick(function() { | ||
this.s = this.s.concat(sub); | ||
cb(null); | ||
}.bind(this)); | ||
setTimeout(cb, getRandomArbitrary(4, 30)); | ||
} | ||
Api.prototype.getError = function getError(text, cb) { | ||
process.nextTick(function() { | ||
setTimeout(function() { | ||
cb(new Error(text)); | ||
}.bind(this)); | ||
}, getRandomArbitrary(4, 30)); | ||
} | ||
Api.prototype.tripleConcat = function callConcat(prefix, cb) { | ||
this.concat(prefix + '1-', function() { | ||
this | ||
.concat(prefix + '2-') | ||
.concat(prefix + '3-', function() { | ||
process.nextTick(function() { | ||
setTimeout(cb, getRandomArbitrary(4, 20)); | ||
}) | ||
}); | ||
}); | ||
} | ||
Api.prototype.call = function call(cb) { | ||
process.nextTick(cb); | ||
} | ||
Api.prototype.multiConcat = function(prefix, cb) { | ||
this | ||
.concat('a-', function() { | ||
this | ||
.concat('b-', function() { | ||
this.concat('c-', function() { | ||
this.concat('d-').concat('e-', function() { | ||
this.concat('f-', function() { | ||
this.concat('g-').concat('h-').concat('i-', function() { | ||
this.concat('j-') | ||
}) | ||
}) | ||
}).concat('k-') | ||
}).concat('l-') | ||
}) | ||
.concat('m-') | ||
.concat('n-') | ||
}) | ||
.concat('o-', function() { | ||
this.concat('p-').concat('q-', function() { | ||
setTimeout(cb, getRandomArbitrary(10, 20)) | ||
}) | ||
}); | ||
} | ||
function getRandomArbitrary(min, max) { | ||
return Math.random() * (max - min) + min; | ||
} |
12187
11
317
89