nicely

a minimalist asynchronous handler, inspired by _.after from underscore.

npm install nicely
117 downloads in the last week
180 downloads in the last month

nicely

Build Status

a minimalist asynchronous handler, inspired by _.after from underscore.

install

npm

$ npm install nicely

github

$ git clone https://github.com/skeggse/nicely.git

test

remember to npm install!

nicely$ mocha
nicely$ npm test
nicely$ make test

api

nicely(times, fn)

When you wish to collect the results of a bunch of asynchronous operations in an object, use nicely.

The primary export, which collects results in an object. Use as follows:

var nicely = require('nicely');

var next = nicely(4, function done(err, object) {
  if (err)
    return console.log('there was an error!', err);
  console.log('something good happened!', object);
});

// next(field) returns after(err, result)

doSomethingFailureProne('getPass', next('pass'));
doSomethingFailureProne('getName', next('name'));
doSomethingFailureProne('getEmail', next('email'));
doSomethingFailureProne('getPhone', next('phone'));

// if all doSomethingFailureProne succeed:
//   "something good happened!", object
// if >= 1 doSomethingFailureProne fail:
//   "there was an error!", err

next(field)

Returns after(err, result) with a closure for field.

Call next when providing an operation with a callback.

after(err, result)

Invokes done if times has been reached.

Call after once an operation is complete (or let the operation do the dirty work).

done(err, result)

User-defined function which should accept err and result.

When an error occurs, done gets called with two parameters: err and field (where field represents the task that failed).

When everything succeeds, done gets called with two parameters: null and result.

nicely.simply(times, fn)

When you wish to handle errors of a bunch of asynchronous operations, but don't care about their results, use simply.

Example:

var nicely = require('nicely');

var next = nicely(4, function done(err) {
  if (err)
    return console.log('there was an error!', err);
  console.log('something good happened!');
});

// next(err) alerts nicely of the completion or failure of an operation

doSomethingFailureProne('getPass', next);
doSomethingFailureProne('getName', next);
doSomethingFailureProne('getEmail', next);
doSomethingFailureProne('getPhone', next);

// if all doSomethingFailureProne succeed:
//   "something good happened!"
// if >= 1 doSomethingFailureProne fail:
//   "there was an error!", err

next(err)

Invokes done if times has been reached or if err is truthy.

Call next once an operation is complete, or pass it as the callback to an operation.

done(err)

User-defined function which should accept err.

When an error occurs, done gets called with err.

When everything succeeds, done gets called with null.

nicely.directly(times, fn)

When you wish to collect the results of a bunch of asynchronous operations in an array, in-order, use directly.

A little simpler than nicely(times, fn), this collects results in an array:

var nicely = require('nicely');

var next = nicely.directly(4, function done(err, array) {
  if (err)
    return console.log('there was an error!', err);
  console.log('something good happened!', array);
});

// next(err, result) appends to the result

doSomethingFailureProne('getPass', next);
doSomethingFailureProne('getName', next);
doSomethingFailureProne('getEmail', next);
doSomethingFailureProne('getPhone', next);

// if all doSomethingFailureProne succeed:
//   "something good happened!", array
// if >= 1 doSomethingFailureProne fail:
//   "there was an error!", err

next(err, result)

Invokes done if times has been reached.

Call next once an operation is complete, or pass it as the callback to an operation.

done(err, result)

User-defined function which should accept err and result.

When an error occurs, done gets called with two parameters: err and index (where index represents the task that failed).

When everything succeeds, done gets called with two parameters: null and result.

nicely.sequentially(fn)

When you wish to execute a sequence of asynchronous operations, one at a time, and collect the results in an object, use nicely.sequentially.

Unlike nicely and nicely.directly, nicely.sequentially does not accept a times parameter. Instead, all operations added with queue will complete--unless one fails--before fn is called.

As an added bonus, both fn and the queued tasks will have the this object set to the results object. Additionally, errors throw in the queued tasks will be caught and passed to fn.

Similar to nicely(times, fn), this aggregates results in an object:

var nicely = require('nicely');

var queue = nicely.sequentially(function done(err, object) {
  if (err)
    return console.log('there was an error!', err);
  console.log('something good happened!', object);
});

// queue(name, fn, args...) queues the specified task for execution

queue('password', doSomethingFailureProne, 'getPass');
queue('name', doSomethingFailureProne, 'getName');
queue('email', doSomethingFailureProne, 'getEmail');
queue('phone', doSomethingFailureProne, 'getPhone');

// if all doSomethingFailureProne succeed:
//   "something good happened!", object
// if >= 1 doSomethingFailureProne fail:
//   "there was an error!", err

queue(name, fn, args...)

Invokes fn after all previously queued functions succeed. The name parameter is used both for error handling, and for aggregation.

Call queue to queue the operation for execution.

done(err, results)

User-defined function which should accept err and results.

When an error occurs, done gets called with two parameters: err and name, where name is the name of the operation that failed.

When everything succeeds, done gets called with two parameters: null and results.

note: For those less inclined to type, feel free to use the nicely.sequence alias.

nicely.intently([options], callback)

When you wish to retry a task, use nicely.intently.

This function only executes a single task, unlike the other functions in nicely, but is compatible with all three, and will return a configured, reusable begin function.

The options parameter is optional, but must be an Object if specified, containing zero or more of the following properties:

  • times the number of times to retry before failing, or 0 to retry forever (default 0)
  • backoff the backoff for the delay (default 2.0)
    • every retry causes intently to wait twice as long as the previous retry
  • initial the initial delay between task retries (default 100)
  • maximum the maximum delay between task retries (default 5000)
  • defer determines whether the task executes the instant begin is called (default false)

note: All times are in milliseconds.

Simple Scenario

var nicely = require('nicely');

var begin = nicely.intently({
  times: 3, // default: 0
  backoff: 2, // default
  initial: 100, // default
  maximum: 300, // default 5000
  defer: true // default false
}, function(err, result) {
  if (err)
    return console.log('error: ' + err.message);
  console.log('woo!', result);
});

// begin(fn, args...) executes the specified task

begin(function(message, callback) {
  console.log(message);
  callback(new Error(message));
}, 'i tried!');

console.log('begin trying');

This simple example will print out:

begin trying
i tried!
i tried!
i tried!
error: i tried!

Complex Scenario

var nicely = require('nicely');

var next = nicely.directly(4, function done(err, result) {
  if (err)
    return console.log('there was an error!', err);
  console.log('something good happened!', result);
});

var begin = nicely.intently({
  times: 3, // default: 0
  backoff: 2, // default
  initial: 100, // default
  maximum: 300, // default 5000
  defer: false // default
}, next);

// begin(fn, args...) executes the specified task

begin(doSomethingFailureProne, 'getPass');
begin(doSomethingFailureProne, 'getName');
begin(doSomethingFailureProne, 'getEmail');
begin(doSomethingFailureProne, 'getPhone');

// if all doSomethingFailureProne succeed after at most thrice calls
//   "something good happened!", result
// if >= 1 doSomethingFailureProne fails three times
//   "there was an error!", err

begin(fn, args...)

Invokes fn with the provided args and retries if failure following the guidelines specified above.

Similar to queue(name, fn, args...), but executes fn immediately.

done(err, result)

User-defined function which should accept err and result.

When an error occurs, done gets called with two parameters: err and index (where index represents the task that failed).

When everything succeeds, done gets called with two parameters: null and result.

integration

underscore

Integrate into underscore:

var _ = require('underscore');
var nicely = require('nicely');

_.mixin(nicely._);

// _.nicely === nicely

browser

nicely is browser-ready, and mimics underscore's browser setup mechanism and noConflict function. nicely does not come in a minified form, but feel free to add it to your project as a vendor file and minify it together with your source. nicely is, after all, in the public domain, so no attribution comment is necessary.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>nicely in a browser</title>
  </head>
  <body>
    <script src="nicely.js"></script>
  </body>
</html>

todo

  • add test to ensure result does not change with calls after done()
  • move test's check functions to a unified file
  • intently code improvements (see lib/nicely.js)
    • add formal defer option tests
    • add formal argument handling tests
  • ponder whether to only allow queue-style function additions instead of returning a callback
    • this would allow nicely.directly to aggregate sequentially

questions

  • could make nicely and nicely.directly optionally take times, and infer from calls to next
    • would need to callback only after tick, however, in case user callbacks are synchronous
  • for performance, would detecting and simplifying double-bindings work?

unlicense / public domain

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to [http://unlicense.org/](http://unlicense.org/)

npm loves you