Asynchronous flow control with a functional taste to it
npm install contra![contra.png][logo]
    
> Asynchronous flow control with a functional taste to it
λ aims to stay small and simple, while powerful. Inspired by [async][1] and [lodash][2]. Methods are implemented individually and not as part of a whole. That design helps when considering to export functions individually. If you need all the methods in async, then stick with it. Otherwise, you might want to check λ out!
Feature requests will be considered on a case-by-case basis.
#### Quick Links
- CHANGELOG
- Comparison with async
- Browser Support
- License
#### API
Flow Control
- λ.waterfall
- λ.series
- λ.concurrent
Functional
- λ.each
- λ.each.series
- λ.map
- λ.map.series
- λ.filter
- λ.filter.series
Uncategorized
Install using npm or bower. Or get the [source code][3] and embed that in a tag.
``shell`
npm i contra --save
`shell`
bower i contra --save
You can use it as a Common.JS module, or embed it directly in your HTML.
`js`
var λ = require('contra');
`html`
The only reason contra isn't published as λ directly is to make it easier for you to type.
These are the asynchronous flow control methods provided by λ.
Executes tasks in series. Each step receives the arguments from the previous step.
- tasks Array of functions with the (...results, next) signaturedone
- Optional function with the (err, ...results) signature
`js`
λ.waterfall([
function (next) {
next(null, 'params for', 'next', 'step');
},
function (a, b, c, next) {
console.log(b);
// <- 'next'
next(null, 'ok', 'done');
}
], function (err, ok, result) {
console.log(result);
// <- 'done'
});
Executes tasks concurrently. Results get passed as an array or hash to an optional done callback. Task order is preserved in results. You can set a concurrency cap, and it's uncapped by default.
- tasks Collection of functions with the (cb) signature. Can be an array or an objectcap
- Optional concurrency level, used by the internal queuedone
- Optional function with the (err, results) signature
`js`
λ.concurrent([
function (cb) {
setTimeout(function () {
cb(null, 'boom');
}, 1000);
},
function (cb) {
cb(null, 'foo');
}
], function (err, results) {
console.log(results);
// <- ['boom', 'foo']
});
Using objects
`js`
λ.concurrent({
first: function (cb) {
setTimeout(function () {
cb(null, 'boom');
}, 1000);
},
second: function (cb) {
cb(null, 'foo');
}
}, function (err, results) {
console.log(results);
// <- { first: 'boom', second: 'foo' }
});
Effectively an alias for λ.concurrent(tasks, 1, done?).
Executes tasks in series. done gets all the results. Results get passed as an array or hash to an optional done callback. Task order is preserved in results.
- tasks Collection of functions with the (next) signature. Can be an array or an objectdone
- Optional function with the (err, results) signature
`js`
λ.series([
function (next) {
setTimeout(function () {
next(null, 'boom');
}, 1000);
},
function (next) {
next(null, 'foo');
}
], function (err, results) {
console.log(results);
// <- ['boom', 'foo']
});
Using objects
`js`
λ.series({
first: function (next) {
setTimeout(function () {
next(null, 'boom');
}, 1000);
},
second: function (next) {
next(null, 'foo');
}
}, function (err, results) {
console.log(results);
// <- { first: 'boom', second: 'foo' }
});
Applies an iterator to each element in the collection concurrently.
- items Collection of items. Can be an array or an objectcap
- Optional concurrency level, used by the internal queueiterator(item, key?, cb)
- Function to execute on each itemitem
- The current itemkey
- Optional, array/object key of the current itemcb
- Needs to be called when processing for current item is donedone
- Optional function with the (err) signature
`js`
λ.each({ thing: 900, another: 23 }, function (item, cb) {
setTimeout(function () {
console.log(item);
cb();
}, item);
});
// <- 23
// <- 900
Effectively an alias for λ.each(items, 1, iterator, done?).
Applies an iterator to each element in the collection concurrently. Produces an object with the transformation results. Task order is preserved in the results.
- items Collection of items. Can be an array or an objectcap
- Optional concurrency level, used by the internal queueiterator(item, key?, cb)
- Function to execute on each itemitem
- The current itemkey
- Optional, array/object key of the current itemcb
- Needs to be called when processing for current item is donedone
- Optional function with the (err, results) signature
`js`
λ.map({ thing: 900, another: 23 }, function (item, cb) {
setTimeout(function () {
cb(null, item * 2);
}, item);
}, function (err, results) {
console.log(results);
<- { thing: 1800, another: 46 }
});
Effectively an alias for λ.map(items, 1, iterator, done?).
Applies an iterator to each element in the collection concurrently. Produces an object with the filtered results. Task order is preserved in results.
- items Collection of items. Can be an array or an objectcap
- Optional concurrency level, used by the internal queueiterator(item, key?, cb)
- Function to execute on each itemitem
- The current itemkey
- Optional, array/object key of the current itemcb
- Needs to be called when processing for current item is doneerr
- An optional error which will short-circuit the filtering process, calling donekeep
- Truthy will keep the item. Falsy will remove it in the resultsdone
- Optional function with the (err, results) signature
`js`
λ.filter({ thing: 900, another: 23, foo: 69 }, function (item, cb) {
setTimeout(function () {
cb(null, item % 23 === 0);
}, item);
}, function (err, results) {
console.log(results);
<- { another: 23, foo: 69 }
});
Effectively an alias for λ.filter(items, 1, iterator, done?).
Used to create a job queue.
- worker(job, done) Function to process jobs in the queuejob
- The current jobdone
- Needs to be called when processing for current job is donecap
- Optional concurrency level, defaults to 1 (serial)
Returns a queue you can push or unshift jobs to. You can pause and resume the queue by hand.
- push(job, done?) Array of jobs or an individual job object. Enqueue those jobs, continue processing (unless paused). Optional callback to run when each job is completedunshift(job, done?)
- Array of jobs or an individual job object. Add jobs to the top of the queue, continue processing (unless paused). Optional callback to run when each job is completedpending
- Property. Jobs that haven't started processing yetlength
- Short-hand for pending.length, only works if getters can be definedpause()
- Stop processing jobs. Those already being processed will run to completionresume()
- Start processing jobs again, after a pause()on('drain', fn)
- Execute fn whenever there's no more pending _(or running)_ jobs and processing is requested. Processing can be requested using resume, push, or unshift
`js
var q = λ.queue(worker);
function worker (job, done) {
console.log(job);
done(null);
}
q.push('job', function () {
console.log('this job is done!');
});
q.push(['some', 'more'], function () {
console.log('one of these jobs is done!');
});
q.on('drain', function () {
console.log('all done!');
// if you enqueue more tasks now, then drain
// will fire again when pending.length reaches 0
});
// <- 'this job is done!'
// <- 'one of these jobs is done!'
// <- 'one of these jobs is done!'
// <- 'all done!'
`
Augments thing with the event emitter methods listed below. If thing isn't provided, an event emitter is created for you. Emitter methods return the thing for chaining.
- thing Optional. Writable JavaScript objectemit(type, ...arguments)
- Emits an event of type type, passing any ...argumentsemitterSnapshot(type)
- Returns a function you can call, passing any ...argumentson(type, fn)
- Registers an event listener fn for type eventsonce(type, fn)
- Same as on, but the listener is discarded after one callbackoff(type, fn)
- Unregisters an event listener fn from type eventsoff(type)
- Unregisters all event listeners from type eventsoff()
- Unregisters all event listeners
The emitterSnapshot(type) method lets you remove all event listeners before emitting an event that might add more event listeners which shouldn't be removed. In the example below, thing removes all events and then emits a 'destroy' event, resulting in a 'create' event handler being attached. If we just used thing.off() after emitting the destroy event, the 'create' event handler would be wiped out too _(or the consumer would have to know implementation details as to avoid this issue)_.
`js
var thing = λ.emitter();
thing.on('foo', foo);
thing.on('bar', bar);
thing.on('destroy', function () {
thing.on('create', reinitialize);
});
var destroy = thing.emitterSnapshot('destroy');
thing.off();
destroy();
`
The emitter can be configured with the following options, too.
- async Debounce listeners asynchronously. By default they're executed in sequence.throws
- Throw an exception if an error event is emitted and no listeners are defined. Defaults to true.
`js
var thing = λ.emitter(); // also, λ.emitter({ foo: 'bar' })
thing.once('something', function (level) {
console.log('something FIRST TROLL');
});
thing.on('something', function (level) {
console.log('something level ' + level);
});
thing.emit('something', 4);
thing.emit('something', 5);
// <- 'something FIRST TROLL'
// <- 'something level 4'
// <- 'something level 5'
`
Returns thing.
Events of type error have a special behavior. λ.emitter will throw if there are no error listeners when an error event is emitted. This behavior can be turned off setting throws: false in the options.
`js
var thing = { foo: 'bar' };
λ.emitter(thing);
thing.emit('error', 'foo');
<- throws 'foo'
`
If an 'error' listener is registered, then it'll work just like any other event type.
`js
var thing = { foo: 'bar' };
λ.emitter(thing);
thing.on('error', function (err) {
console.log(err);
});
thing.emit('error', 'foo');
<- 'foo'
`
Returns a function bound with some arguments and a next callback.
`js`
λ.curry(fn, 1, 3, 5);
// <- function (next) { fn(1, 3, 5, next); }
[async][1]|λapply
---|---
Aimed at Noders|Tailored for browsers
Arrays for [some][5], collections for [others][6]|Collections for everyone!|curryparallel|concurrentparallelLimit|concurrentmapSeries|map.series~29.6k (minified, uncompressed)
More _comprehensive_|More _focused_|~2.7k (minified, uncompressed)
λ isn't meant to be a replacement for async. It aims to provide a more focused library, and a bit more consistency.

If you need support for one of the legacy browsers listed below, you'll need contra.shim.js.
- IE < 10
- Safari < 6
- Opera < 16
`js`
require('contra/shim');
var λ = require('contra');
`html`
The shim currently clocks around ~1.2k` minified, uncompressed.
MIT
[logo]: https://raw.github.com/bevacqua/contra/master/resources/contra.png
[1]: https://github.com/caolan/async
[2]: https://github.com/lodash/lodash
[3]: https://github.com/bevacqua/contra/tree/master/src/contra.js
[4]: https://github.com/bevacqua
[5]: https://github.com/caolan/async#maparr-iterator-callback
[6]: https://github.com/caolan/async#paralleltasks-callback