contra

:surfer: Asynchronous flow control with a functional taste to it

Subscribe to updates I use contra


Statistics on contra

Number of watchers on Github 714
Number of open issues 0
Average time to close an issue 13 days
Main language JavaScript
Average time to merge a PR about 3 hours
Open pull requests 0+
Closed pull requests 1+
Last commit over 1 year ago
Repo Created over 3 years ago
Repo Last Updated 4 months ago
Size 272 KB
Homepage https://ponyfoo.com
Organization / Authorbevacqua
Contributors5
Page Updated
Do you use contra? Leave a review!
View contra activity
View on github
Latest Open Source Launches
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating contra for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

contra.png

badge badge badge help me on gittip flattr.png

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

API

Flow Control

Functional

Uncategorized

Install

Install using npm or bower. Or get the source code and embed that in a <script> tag.

npm i contra --save
bower i contra --save

You can use it as a Common.JS module, or embed it directly in your HTML.

var  = require('contra');
<script src='contra.js'></script>
<script>
var  = contra;
</script>

The only reason contra isn't published as `` directly is to make it easier for you to type.

Back to top

API

These are the asynchronous flow control methods provided by ``.

.waterfall(tasks, done?)

Executes tasks in series. Each step receives the arguments from the previous step.

  • tasks Array of functions with the (...results, next) signature
  • done Optional function with the (err, ...results) signature
.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'
});

Back to top

.concurrent(tasks, cap?, 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 object
  • cap Optional concurrency level, used by the internal queue
  • done Optional function with the (err, results) signature
.concurrent([
  function (cb) {
    setTimeout(function () {
      cb(null, 'boom');
    }, 1000);
  },
  function (cb) {
    cb(null, 'foo');
  }
], function (err, results) {
  console.log(results);
  // <- ['boom', 'foo']
});

Using objects

.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' }
});

Back to top

.series(tasks, done?)

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 object
  • done Optional function with the (err, results) signature
.series([
  function (next) {
    setTimeout(function () {
      next(null, 'boom');
    }, 1000);
  },
  function (next) {
    next(null, 'foo');
  }
], function (err, results) {
  console.log(results);
  // <- ['boom', 'foo']
});

Using objects

.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' }
});

Back to top

.each(items, cap?, iterator, done?)

Applies an iterator to each element in the collection concurrently.

  • items Collection of items. Can be an array or an object
  • cap Optional concurrency level, used by the internal queue
  • iterator(item, key?, cb) Function to execute on each item
    • item The current item
    • key Optional, array/object key of the current item
    • cb Needs to be called when processing for current item is done
  • done Optional function with the (err) signature
.each({ thing: 900, another: 23 }, function (item, cb) {
  setTimeout(function () {
    console.log(item);
    cb();
  }, item);
});
// <- 23
// <- 900

Back to top

.each.series(items, iterator, done?)

Effectively an alias for .each(items, 1, iterator, done?).

Back to top

.map(items, cap?, 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 object
  • cap Optional concurrency level, used by the internal queue
  • iterator(item, key?, cb) Function to execute on each item
    • item The current item
    • key Optional, array/object key of the current item
    • cb Needs to be called when processing for current item is done
  • done Optional function with the (err, results) signature
.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 }
});

Back to top

.map.series(items, iterator, done?)

Effectively an alias for .map(items, 1, iterator, done?).

Back to top

.filter(items, cap?, 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 object
  • cap Optional concurrency level, used by the internal queue
  • iterator(item, key?, cb) Function to execute on each item
    • item The current item
    • key Optional, array/object key of the current item
    • cb Needs to be called when processing for current item is done
    • err An optional error which will short-circuit the filtering process, calling done
    • keep Truthy will keep the item. Falsy will remove it in the results
  • done Optional function with the (err, results) signature
.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 }
});

Back to top

.filter.series(items, iterator, done?)

Effectively an alias for .filter(items, 1, iterator, done?).

Back to top

.queue(worker, cap=1)

Used to create a job queue.

  • worker(job, done) Function to process jobs in the queue
    • job The current job
    • done Needs to be called when processing for current job is done
  • cap 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 completed
  • unshift(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 completed
  • pending Property. Jobs that haven't started processing yet
  • length Short-hand for pending.length, only works if getters can be defined
  • pause() Stop processing jobs. Those already being processed will run to completion
  • resume() 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
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!'

Back to top

.emitter(thing={}, options={})

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 object
  • emit(type, ...arguments) Emits an event of type type, passing any ...arguments
  • emitterSnapshot(type) Returns a function you can call, passing any ...arguments
  • on(type, fn) Registers an event listener fn for type events
  • once(type, fn) Same as on, but the listener is discarded after one callback
  • off(type, fn) Unregisters an event listener fn from type events
  • off(type) Unregisters all event listeners from type events
  • off() 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).

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.
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.

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.

var thing = { foo: 'bar' };

.emitter(thing);

thing.on('error', function (err) {
  console.log(err);
});

thing.emit('error', 'foo');
<- 'foo'

Back to top

.curry(fn, ...arguments)

Returns a function bound with some arguments and a next callback.

.curry(fn, 1, 3, 5);
// <- function (next) { fn(1, 3, 5, next); }

Back to top

Comparison with async

async ``
Aimed at Noders Tailored for browsers
Arrays for some, collections for others Collections for everyone!
apply curry
parallel concurrent
parallelLimit concurrent
mapSeries map.series
More comprehensive More focused
~29.6k (minified, uncompressed) ~2.7k (minified, uncompressed)

`isn't meant to be a replacement forasync`. It aims to provide a more focused library, and a bit more consistency.

Back to top

Browser Support

Browser Support

If you need support for one of the legacy browsers listed below, you'll need contra.shim.js.

  • IE < 10
  • Safari < 6
  • Opera < 16
require('contra/shim');
var  = require('contra');
<script src='contra.shim.js'></script>
<script src='contra.js'></script>
<script>
var  = contra;
</script>

The shim currently clocks around ~1.2k minified, uncompressed.

Back to top

License

MIT

Back to top

contra questions on Stackoverflow (View All Questions)
  • Pro/contra of using "else" if the then-clause exits the method anyways
  • Based on Excel VBA, is it better to create ADODB objects Or to use DOCMD.RUNSQL? Which are the pro & contra?
  • unexpected behavior in covariance/contra-variance like assignment of typescript
  • Why aren't there many discussions about co- and contra-variance in Haskell (as opposed to Scala or C#)?
  • Difference between Covariance & Contra-variance
  • Turret Logic like in Contra - XNA
  • Contra/covariance issues with generics
  • ERD design pro and contra
  • Isn't the argument type co- not contra-variant?
  • How are co- and contra-variance used in designing business applications?
  • Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work?
  • Why does C#/CLR not support method override co/contra-variance?
  • Creating contra values in the same column SQL
  • Functors relationship in co and contra variance?
  • Form code on fly and eval() it for repeatable blocks of code - PRO & CONTRA?
  • Can parameters be contra- or covariant in Python?
  • What's a common programming use of contra-variance?
  • Java, strong typing, covariance and contra-variance
  • Scala compiler co- and contra-variance rules
  • CoVariance Contra Variance
  • URL Rewriting contra Making Folders with index files
  • Subversion: pro and contra of putting svn keywords into all source file headers
  • Contra variance with interfaces
  • Why is there no parameter contra-variance for overriding?
  • .NET equivalent for Java wildcard generics <?> with co- and contra- variance?
  • Pro and contra, haskell lazy method
  • java co-variance/contra-variance
  • Scala - Co/Contra-Variance as applied to implicit parameter selection
  • Override contra-variance workaround needed
  • Can't get co/contra-variance to work in C#
contra list of languages used
Other projects in JavaScript