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

Related tags

Control Flow contra
Overview

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 and lodash. 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 for async. 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

You might also like...

Plain functions for a more functional Deku approach to creating stateless React components, with functional goodies such as compose, memoize, etc... for free.

Plain functions for a more functional Deku approach to creating stateless React components, with functional goodies such as compose, memoize, etc... for free.

"Keo" is the Vietnamese translation for glue. Plain functions for a more functional Deku approach to creating stateless React components, with functio

Sep 24, 2022

Web based application that uses playerctl in it backend to control remotely your audio using the frontend as remote control.

Web based application that uses playerctl in it backend to control remotely your audio using the frontend as remote control.

Linux Remote This is a web based application that uses playerctl in it backend to control remotely your audio using the frontend as remote control. Do

Jul 6, 2022

A three.js and roslibjs powered web-control for zju fast-drone-250 for laptop-free flight control

A three.js and roslibjs powered web-control for zju fast-drone-250 for laptop-free flight control

Web Control for ZJU Fast-Drone-250 A three.js and roslibjs powered web-control for zju fast-drone-250 for laptop-free flight control (tested on Xiaomi

Nov 11, 2022

CryptoDappy is the fastest way to get started with Flow.

CryptoDappy is the fastest way to get started with Flow.

Demo application (testnet) Learning hub (start learning here) What's CryptoDappy? CryptoDappy is the fastest way to get started with blockchain develo

Aug 18, 2022

A basic React/NextJS project showing how to use the Flow Client Library (FCL)

A basic React/NextJS project showing how to use the Flow Client Library (FCL)

How to use the Flow Client Library (FCL) with SvelteKit Everything you need to build a SvelteKit project with the Flow Client Library (FCL). For a Sve

Sep 24, 2022

Drawflow - Simple flow library 🖥️🖱️

Drawflow - Simple flow library 🖥️🖱️

Drawflow Simple flow library. Drawflow allows you to create data flows easily and quickly. Installing only a javascript library and with four lines of

Jan 8, 2023

One-page checkout flow

Medusa Express Medusa is an open-source headless commerce engine that enables developers to create amazing digital commerce experiences. Built with Me

Oct 11, 2022

Marquee is a VS Code extension designed to naturally integrate with your development flow, so that you will no longer lose track of your thoughts while you're coding

Marquee Stay organized with minimal context switching, all inside your Visual Studio Code. Marquee is a VS Code extension designed to naturally integr

Dec 13, 2022

A simple NEXT.js app that lists NFTs within a contract address from a Buildable Flow.

NFT Marketplace Demo This is a basic Next.js app for listing NFTs in a given contract address. The purpose of this repository is to showcase the simpl

Dec 12, 2022

A Flow-based programming language for universal applications.

A Flow-based programming language for universal applications.

Hlang A Flow-based programming language for universal applications. Hlang aims to make programming easier, faster and more comfortable. It avoids codi

Dec 25, 2022

Import flow for Excel (.xlsx) and CSV file with automated column matching and validation.

Import flow for Excel (.xlsx) and CSV file with automated column matching and validation.

RSI react-spreadsheet-import ⚡️ A component used for importing XLS / XLSX / CSV documents built with Chakra UI. Import flow combines: 📥 Uploader ⚙️ P

Dec 24, 2022

Marry in Web3, Mint Paired Soulbound NFTs by MultiSign Flow, No transfer, No sell, a non-financial Dapp

Marry in Web3,  Mint Paired Soulbound NFTs  by MultiSign Flow, No transfer, No sell, a non-financial Dapp

ERC721-520 Token 是 NFT-like Soulbound Token Standard(灵魂绑定凭证) 的一种实现,是 ERC721 标准的扩展。 ERC721-520 Token 不可转让,不可售卖,一个人同时只能有一个有效 Token ERC721-520 Token 由二者通

Dec 21, 2022

MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are still part of a flow in the AWS cloud remote.

MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are still part of a flow in the AWS cloud remote.

MerLoc MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are

Dec 21, 2022

⚛️ Hooks for fetching, caching and updating asynchronous data in React

⚛️ Hooks for fetching, caching and updating asynchronous data in React

Hooks for fetching, caching and updating asynchronous data in React Enjoy this library? Try the entire TanStack! React Table, React Form, React Charts

Jan 9, 2023

MongoDB object modeling designed to work in an asynchronous environment.

MongoDB object modeling designed to work in an asynchronous environment.

Mongoose Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks. Do

Dec 30, 2022

🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️

🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️

A reactive database framework Build powerful React and React Native apps that scale from hundreds to tens of thousands of records and remain fast ⚡️ W

Jan 5, 2023

⚛️ Hooks for fetching, caching and updating asynchronous data in React

⚛️ Hooks for fetching, caching and updating asynchronous data in React

Hooks for fetching, caching and updating asynchronous data in React Enjoy this library? Try the entire TanStack! React Table, React Form, React Charts

Dec 31, 2022

Asynchronous Javascript templating for the browser and server

Dust.js Asynchronous Javascript templating for the browser and server. This fork is maintained by LinkedIn. Install NPM Important: We recommend that y

Dec 31, 2022

Asynchronous Javascript templating for the browser and server

Dust.js Asynchronous Javascript templating for the browser and server. This fork is maintained by LinkedIn. Install NPM Important: We recommend that y

Dec 31, 2022
Comments
  • Modularize?

    Modularize?

    λ aims to be modular and browser-friendly. How about releasing every function as a separate script? This goes right in hand with “Methods are implemented individually and not as part of a whole.”

    Here’s why: when you just want one or two λ functions, you still have to pull the whole source of contra into your bundle:

    var concurrent = require("contra").concurrent;
    

    3 kB minified code isn’t much – but it matters when you write a browser-facing app or library. It would be much better if you could just get the 300 bytes you really need:

    var concurrent = require("contra/concurrent");
    

    Here’s how: tjmehta/101 is a great example of such an approach.

    You can also keep full backwards compatibility with the current syntax – var concurrent = require("contra").concurrent – by supplying an index.js like this one. This index.js could then also be compiled into a drop-in bundle for browsers. Just as it is now.

    opened by tomek-he-him 5
  • Changed property throws accessor

    Changed property throws accessor

    YUI compressor currently fails with error : "missing name after . operator"

    This fixes it and allows it to compress correctly, with no errors

    opened by JohnyDays 1
  • Bump uglify-js from 2.4.23 to 3.6.8

    Bump uglify-js from 2.4.23 to 3.6.8

    Bumps uglify-js from 2.4.23 to 3.6.8.

    Release notes

    Sourced from uglify-js's releases.

    v3.6.8

     

    v3.6.7

     

    v3.6.6

     

    v3.6.5

     

    v3.6.4

     

    v3.6.3

     

    v3.6.2

     

    v3.6.1

     

    v3.6.0

     

    v3.5.15

     

    v3.5.14

     

    v3.5.13

     

    v3.5.12

     

    v3.5.11

     

    v3.5.10

     

    v3.5.9

     

    v3.5.8

     

    ... (truncated)
    Commits
    Maintainer changes

    This version was pushed to npm by alexlamsl, a new releaser for uglify-js since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Owner
Nicolás Bevacqua
🎉 Engineering @elastic 📚 Published @mjavascript 📔 Published @buildfirst ✍ Blog @ponyfoo ⛺ Organized @nodeconf @beerjs ⛵ Conference Speaker 🛬
Nicolás Bevacqua
The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)

co Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way. Co v4 co@4

TJ Holowaychuk 11.8k Jan 2, 2023
Callback-free control flow for Node using ES6 generators.

suspend Generator-based control-flow for Node enabling asynchronous code without callbacks, transpiling, or selling your soul. Suspend is designed to

Jeremy Martin 550 Jul 9, 2022
Flow control and error handling for Node.js

NOTE: This project is deprecated and no longer being actively developed or maintained. See Issue #50 for details. StrongLoop zone library Overview The

StrongLoop and IBM API Connect 280 Feb 18, 2022
Kyrillos Hany 14 Aug 10, 2022
An async control-flow library that makes stepping through logic easy.

Step A simple control-flow library for node.JS that makes parallel execution, serial execution, and error handling painless. How to install Simply cop

Tim Caswell 2.2k Dec 22, 2022
The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)

co Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way. Co v4 co@4

TJ Holowaychuk 11.8k Jan 2, 2023
Callback-free control flow for Node using ES6 generators.

suspend Generator-based control-flow for Node enabling asynchronous code without callbacks, transpiling, or selling your soul. Suspend is designed to

Jeremy Martin 550 Jul 9, 2022
Flow control and error handling for Node.js

NOTE: This project is deprecated and no longer being actively developed or maintained. See Issue #50 for details. StrongLoop zone library Overview The

StrongLoop and IBM API Connect 280 Feb 18, 2022
Collection of CSS box-shadows for every taste contains more than 100 simple, beautiful and airy shadows.

Vue Box-shadows Collection of CSS box-shadows for every taste contains more than 100 simple, beautiful and airy shadows. Add beautiful shadow effects

Andrej Sharapov 29 Nov 14, 2022
A powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)

Nunjucks Nunjucks is a full featured templating engine for javascript. It is heavily inspired by jinja2. View the docs here. Installation npm install

Mozilla 8k Dec 30, 2022