callback-patterns

A collection of design patterns for callback-driven async code

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
callback-patterns
153.0.02 years ago5 years agoMinified + gzip package size for callback-patterns in KB

Readme

callback-patterns
callback-patterns is a collection of design patterns for callback-driven async code. The design patterns in this library are constructors that build callback-expecting functions. Each pattern is designed to be a stand-alone piece of code, tested for performance and robustness.

Design

There is one major difference between this package and many other callback-driven async libraries: the callback comes first.
// this
function task(callback, arg1, arg2, ...) { }

// not this
function task(arg1, arg2, ..., callback) { }

This makes it easier to compose callback-driven functions in useful ways, with almost no boilerplate code.

API

callback-patterns : object

Kind: global namespace
* [.testing](#callback-patterns.testing) : <code>object</code>
    * [.AssertionTest](#callback-patterns.testing.AssertionTest)
        * [new AssertionTest()](#new_callback-patterns.testing.AssertionTest_new)
        * [assertionTest.describe(description)](#callback-patterns.testing.AssertionTest+describe) ⇒ <code>AssertionTest</code>
        * [assertionTest.setup(task)](#callback-patterns.testing.AssertionTest+setup) ⇒ <code>AssertionTest</code>
        * [assertionTest.prepare(task)](#callback-patterns.testing.AssertionTest+prepare) ⇒ <code>AssertionTest</code>
        * [assertionTest.execute(task)](#callback-patterns.testing.AssertionTest+execute) ⇒ <code>AssertionTest</code>
        * [assertionTest.verify(...tasks)](#callback-patterns.testing.AssertionTest+verify) ⇒ <code>AssertionTest</code>
        * [assertionTest.teardown(task)](#callback-patterns.testing.AssertionTest+teardown) ⇒ <code>AssertionTest</code>
        * [assertionTest.build()](#callback-patterns.testing.AssertionTest+build) ⇒ <code>function</code>
        * [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)
        * [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)
* [.unstable](#callback-patterns.unstable) : <code>object</code>
    * [.TraceError(task)](#callback-patterns.unstable.TraceError) ⇒ <code>CallbackTask</code>
* [.Assert(validator, message)](#callback-patterns.Assert) ⇒ <code>CallbackTask</code>
* [.Background(backgroundTask)](#callback-patterns.Background) ⇒ <code>CallbackTask</code>
* [.Bridge(task)](#callback-patterns.Bridge) ⇒ <code>function</code>
* [.Callbackify(generator)](#callback-patterns.Callbackify) ⇒ <code>CallbackTask</code>
* [.CatchError(task)](#callback-patterns.CatchError) ⇒ <code>CallbackTask</code>
* [.Delay(delay)](#callback-patterns.Delay) ⇒ <code>CallbackTask</code>
* [.If(ifTask, thenTask, elseTask)](#callback-patterns.If) ⇒ <code>CallbackTask</code>
* [.InOrder(...tasks)](#callback-patterns.InOrder) ⇒ <code>CallbackTask</code>
* [.InParallel(...tasks)](#callback-patterns.InParallel) ⇒ <code>CallbackTask</code>
    * [.Flatten(...tasks)](#callback-patterns.InParallel.Flatten) ⇒ <code>CallbackTask</code>
* [.InSeries(...tasks)](#callback-patterns.InSeries) ⇒ <code>CallbackTask</code>
* [.Logging(...statements)](#callback-patterns.Logging) ⇒ <code>CallbackTask</code>
* [.Memoize(CallbackTask, [keyFunction], [cache])](#callback-patterns.Memoize) ⇒ <code>CallbackTask</code>
* [.ParallelFilter(filter)](#callback-patterns.ParallelFilter) ⇒ <code>CallbackTask</code>
* [.ParallelMap(task)](#callback-patterns.ParallelMap) ⇒ <code>CallbackTask</code>
* [.PassThrough()](#callback-patterns.PassThrough)
* [.Promisify(task)](#callback-patterns.Promisify) ⇒ <code>function</code>
* [.Race(...tasks)](#callback-patterns.Race) ⇒ <code>CallbackTask</code>
* [.Retry(task, retryStrategy)](#callback-patterns.Retry) ⇒ <code>CallbackTask</code>
* [.Throttle(task, limit)](#callback-patterns.Throttle) ⇒ <code>CallbackTask</code>
* [.TimeIn(task, ms)](#callback-patterns.TimeIn) ⇒ <code>CallbackTask</code>
* [.TimeOut(task, ms)](#callback-patterns.TimeOut) ⇒ <code>CallbackTask</code>
* [.Timer(task, label)](#callback-patterns.Timer) ⇒ <code>CallbackTask</code>
* [.While(conditionTask, loopTask)](#callback-patterns.While) ⇒ <code>function</code>

callback-patterns.testing : object

Kind: static namespace of callback-patterns
* [.AssertionTest](#callback-patterns.testing.AssertionTest)
    * [new AssertionTest()](#new_callback-patterns.testing.AssertionTest_new)
    * [assertionTest.describe(description)](#callback-patterns.testing.AssertionTest+describe) ⇒ <code>AssertionTest</code>
    * [assertionTest.setup(task)](#callback-patterns.testing.AssertionTest+setup) ⇒ <code>AssertionTest</code>
    * [assertionTest.prepare(task)](#callback-patterns.testing.AssertionTest+prepare) ⇒ <code>AssertionTest</code>
    * [assertionTest.execute(task)](#callback-patterns.testing.AssertionTest+execute) ⇒ <code>AssertionTest</code>
    * [assertionTest.verify(...tasks)](#callback-patterns.testing.AssertionTest+verify) ⇒ <code>AssertionTest</code>
    * [assertionTest.teardown(task)](#callback-patterns.testing.AssertionTest+teardown) ⇒ <code>AssertionTest</code>
    * [assertionTest.build()](#callback-patterns.testing.AssertionTest+build) ⇒ <code>function</code>
    * [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)
    * [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)

testing.AssertionTest

Kind: static class of testing
* [new AssertionTest()](#new_callback-patterns.testing.AssertionTest_new)
* [assertionTest.describe(description)](#callback-patterns.testing.AssertionTest+describe) ⇒ <code>AssertionTest</code>
* [assertionTest.setup(task)](#callback-patterns.testing.AssertionTest+setup) ⇒ <code>AssertionTest</code>
* [assertionTest.prepare(task)](#callback-patterns.testing.AssertionTest+prepare) ⇒ <code>AssertionTest</code>
* [assertionTest.execute(task)](#callback-patterns.testing.AssertionTest+execute) ⇒ <code>AssertionTest</code>
* [assertionTest.verify(...tasks)](#callback-patterns.testing.AssertionTest+verify) ⇒ <code>AssertionTest</code>
* [assertionTest.teardown(task)](#callback-patterns.testing.AssertionTest+teardown) ⇒ <code>AssertionTest</code>
* [assertionTest.build()](#callback-patterns.testing.AssertionTest+build) ⇒ <code>function</code>
* [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)
* [AssertionTest.VerifyErrorWasNotThrown()](#callback-patterns.testing.AssertionTest.VerifyErrorWasNotThrown)

new AssertionTest()
const PingTest = AssertionTest()
  .describe('can ping internet')
  .setup(
    // build our setup
    (next) => {
      const setup = {};
      setup.testHosts = [ 'google.com', 'microsoft.com', 'yahoo.com' ];
      next(null, setup);
    }
  )
  .prepare(
    // run test with first host
    (next, setup) => {
      const host = setup.testHosts[0];
      next(null, host);
    }
  )
  .execute(
    (next, host) => ping.sys.probe(
      host,
      (isAlive, error) => next(error, isAlive)
    )
  )
  .verify(
    // verify no error was thrown
    (next, { setup, request, result, error }) => next(error),
    // verify result is true
    (next, { setup, request, result, error }) => next(
      result !== true ? new Error(`could not ping host ${request}`) : null
    )
  )
  .teardown(
    // nothing to teardown
    (next, { setup, request, result, error }) => next()
  )
  .build();

  test( () => console.log('test done') );
Constructor for an AssertionTest builder.
assertionTest.describe(description) ⇒ AssertionTest
AssertionTest#describe lets you set a description for a test case. This description is part of the label attached to the test case when built.
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • description string - a string label describing the test case

assertionTest.setup(task) ⇒ AssertionTest
AssertionTest#setup gives you a hook to build test fixtures before execution. This is the first step that runs in a test. setup is a separate step from prepare because you often want to use a common setup function to build test fixtures for multiple tests.
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • task function - a setup task function - should return a setup object

assertionTest.prepare(task) ⇒ AssertionTest
AssertionTest#prepare gives you a hook to prepare the request that the test uses to execute. This is the second step that runs in a test, and the last step before execute. The prepare task is passed the results from setup.
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • task function - a prepare task function - should accept a context containing the setup, and return a request object to be given to the executing task

assertionTest.execute(task) ⇒ AssertionTest
AssertionTest#execute lets you specify the task that is executed in a test. The execute task is passed the results from prepare.
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • task function - the task the test should execute, and capture results and errors from

assertionTest.verify(...tasks) ⇒ AssertionTest
AssertionTest#verify lets you specify any number of tasks to verify the test results. Each verify task is passed a complete record of all test fixtures in an object, including the setup, the request, the result, and the error (if an error was thrown)
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • ...tasks function - any number of verification tasks

assertionTest.teardown(task) ⇒ AssertionTest
AssertionTest#teardown gives you a hook to tear down the test fixtures after execution. The teardown task is passed a complete record of all test fixtures in an object, including the setup, the request, the result, and the error (if an error was thrown)
Kind: instance method of AssertionTest
Returns: AssertionTest - this
Params
  • task function - a task to tear down the setup

assertionTest.build() ⇒ function
Builds the test case function.
Kind: instance method of AssertionTest
Returns: function - callback-expecting test function

AssertionTest.VerifyErrorWasNotThrown()
verifier function to make sure test DID NOT throw an error
Kind: static method of AssertionTest

AssertionTest.VerifyErrorWasNotThrown()
verifier function to make sure test DID throw an error
Kind: static method of AssertionTest

callback-patterns.unstable : object

Kind: static namespace of callback-patterns

unstable.TraceError(task) ⇒ CallbackTask

TraceError is an experimental wrapper that attempts to make errors more informative. It does this by appending extra information to the stack of any error thrown in the task.
NOTE: TraceError is marked as 'unstable' as stack traces in JS are not standardized, so it may not always provide useful information.
Kind: static method of unstable
Returns: CallbackTask - a wrapper function that modifies the stack trace of any errors thrown within
Params
  • task CallbackTask - a task function to wrap

Example
let TraceError = require('callback-patterns/unstable/TraceError');
let InSeries = require('callback-patterns/InSeries');

let task = InSeries(
  function(next, ...args) {},
  function(next, ...args) {},
  ...
);

task = TraceError(task);

task(next, ...args);

callback-patterns.Assert(validator, message) ⇒ CallbackTask

Builds an assertion task. When called, if the arguments do not match the validator functions, Assert passes an error to its callback.
Kind: static method of callback-patterns
Returns: CallbackTask - an assertion task
Params
  • validator function - a function that checks the arguments.
  • message string - an optional error message to throw if the assertion fails, or a message builder function.

Example
let Assert = require('callback-patterns/Assert');
let InSeries = require('callback-patterns/InSeries');

let task = InSeries(
  (next, num) => next(null, num),
  Assert(
    (num) => (num >= 0),
    (num) => `${num} is less than zero`
  ),
  (next, num) => next(null, num),
);

let onDone = (err, result) => console.log(err, result);

task(onDone, 1); // prints null 1, eventually
task(onDone, -1); // prints '-1 is less than zero', eventually

callback-patterns.Background(backgroundTask) ⇒ CallbackTask

Background runs a task in the background. It acts like PassThrough, but also schedules the backround task to be called.
NOTE: any error a background task throws is caught and ignored. If you need error handling in a background task, catch the error using CatchError
Kind: static method of callback-patterns
Returns: CallbackTask - a wrapper task that schedules backgroundTask to be run when called.
Params
  • backgroundTask CallbackTask - a task function to be run in the background.

Example
let InSeries = require('callback-patterns/InSeries');
let Background = require('callback-patterns/Background');

let logRequest = (next, ...args) => { ... }; // log a request in some logging service
let loadData = (next, ...args) => { ... }; // load some data for analysis
let buildReport = (next, ...args) => { ... }; // build aggregate statistics report on data
let saveReport = (next, ...args) => { ... }; // dump report into filesystem somewhere as a backup

let fetchReport = InSeries(
   Background(logRequest), // don't wait for the request to finish logging
   loadData,
   buildReport,
   Background(saveReport) // don't wait for the report to be saved before returning it
);

callback-patterns.Bridge(task) ⇒ function

Wraps around a callback-driven function, and returns a function that can be called either with a callback, or as an async function that returns a promise. This makes it easier to bridge the gap between callback-driven code and promise-driven code
NOTE: Bridge works by checking if the first argument is a function, and assuming its a callback if so. You should take care to not use the Bridge wrapper if your task expects the first argument to be a function.
Kind: static method of callback-patterns
Returns: function - a task that can either be passed a callback, or awaited
Params
  • task function - a callback-driven task.

Example
let InSeries = require('callback-patterns/InSeries');
let Bridge = require('callback-patterns/Bridge');

let task = Bridge(
  function(next, ...args) {...},
);

task(next, ...args); // this works

task(...args).then(...); // this works too

callback-patterns.Callbackify(generator) ⇒ CallbackTask

Wraps around a promise generator function, to make it easier to integrate with task functions.
Kind: static method of callback-patterns
Returns: CallbackTask - a task that wraps around the promise
Params
  • generator function - a function that generates a promise from the args.

Example
let InSeries = require('callback-patterns/InSeries');
let Callbackify = require('callback-patterns/Callbackify');

let task = InSeries(
  function(next, ...args) {...},
  Callbackify(
    (...args) => new Promise((resolve, reject) => resolve(...args))
  ),
  function(next, ...args) {},
  ...
);

task(next, ...args);

callback-patterns.CatchError(task) ⇒ CallbackTask

Errors bypass the normal flow of execution. They always return immediately up the "stack", even if they occur inside nested InSeries or InParallel chains.
let InSeries = require('callback-patterns/InSeries');
let CatchError = require('callback-patterns/CatchError');

let task = InSeries(
  (next) => { console.log(1); next(); }
  InSeries(
    (next) => { console.log(2); next(); }
    (next) => { console.log(3); next('Error'); }
  ),
  InSeries(
    (next) => { console.log(4); next();}
    (next) => { console.log(5); next();}
  )
)(console.log); // prints out 1 2 3 Error, eventually

If you need to catch an error explicitly at some point, wrap a task in CatchError, which will return the error as the first argument to the next function.
let InSeries = require('callback-patterns/InSeries');
let CatchError = require('callback-patterns/CatchError');
let task = InSeries(
  (next) => { console.log(1); next();}
  CatchError(
    InSeries(
      (next) => { console.log(2); next();}
      (next) => { console.log(3); next('Error');}
    ),
  ),
  (next, error) => error != null ? console.log('Error Caught') : null,
  InSeries(
    (next) => { console.log(4); next();}
    (next) => { console.log(5); next();}
  )
)(console.log); // prints out 1 2 3 Error Caught 4 5, eventually

Kind: static method of callback-patterns
Returns: CallbackTask - a wrapper function around the task
Params
  • task CallbackTask - a function that checks the arguments.

callback-patterns.Delay(delay) ⇒ CallbackTask

Delay acts like PassThrough, but inserts a delay in the call.
Kind: static method of callback-patterns
Returns: CallbackTask - a delay task
Params
  • delay number - The time to delay, in ms.

Example
let Delay = require('callback-patterns/Delay');
let InSeries = require('callback-patterns/InSeries');

let task = InSeries(
  (next, num) => next(null, num),
  Delay(100),
  (next, num) => next(null, num + 1),
);

let onDone = (err, result) => console.log(err, result);

task(onDone, 1); // prints null 1, after a 100 ms delay

callback-patterns.If(ifTask, thenTask, elseTask) ⇒ CallbackTask

If accepts up to three tasks, an 'if' task, a 'then' task, and lastly an 'else' task note: by default, the ifTask, thenTask, and elseTask are PassThrough note: the ifTask can return multiple results, but only the first is checked for truthiness
Kind: static method of callback-patterns
Params
  • ifTask CallbackTask - a condition task.
  • thenTask CallbackTask - a task to run when ifTask returns a truthy value.
  • elseTask CallbackTask - a task to run when ifTask returns a falsy value.

Example
let If = require('callback-patterns/If');

let logIfEven = If(
  (next, num) => next(null, num % 2 === 0)
  (next, num) => { console.log('is even!'); next(null, num); },
  (next, num) => { console.log('is not even!'); next(null, num); },
);

let onDone = (err, ...results) => console.log(results);

logIfEven(null, 1); // prints out 'is not even!' eventually
logIfEven(null, 2); // prints out 'is even!' eventually

callback-patterns.InOrder(...tasks) ⇒ CallbackTask

let InOrder = require('callback-patterns/InOrder');

let task = InOrder(
  function(next, ...args) {},
  function(next, ...args) {},
  ...
);

task(next, ...args);
Runs several asynchronous tasks one after another. Each task gets the arguments that were originally passed into the wrapper. This is different from InSeries, where the output of each is task is passed as the input to the next.
const InOrder = require('callback-patterns/InOrder');

const task = InOrder(
  (next, a) => { a.val = 1; console.log(a.val); next();}
  (next, a) => { a.val = 2; console.log(a.val); next();}
  (next, a) => { a.val = 3; console.log(a.val); next();}
)(null, {}); // prints out 1 2 3, eventually

Kind: static method of callback-patterns
Returns: CallbackTask - a wrapper function that runs the tasks in order
Params
  • ...tasks CallbackTask - any number of tasks to run in order.

callback-patterns.InParallel(...tasks) ⇒ CallbackTask

let InParallel = require('callback-patterns/InParallel');

let task = InParallel(
  function(next, ...args) {},
  function(next, ...args) {},
  ...
);

task(next, ...args);
InParallel accepts a number of functions, and returns a task function that executes all of its child tasks in parallel.
note: because the callbacks can return any number of results, the results from each task are autoboxed into an array. This includes an empty array for tasks that don't return results.
Kind: static method of callback-patterns
Returns: CallbackTask - a wrapper function that runs the tasks in parallel
Params
  • ...tasks CallbackTask - any number of tasks to run in parallel.

Example
let InParallel = require('callback-patterns/InParallel');

let task = InParallel(
  (next) => next(null, 1),
  (next) => next(null, 2),
  (next) => next(null, 3, 4),
);

let onDone = (err, ...results) => console.log(results);

chain(onDone); // prints [ [ 1 ], [ 2 ], [ 3, 4 ] ]

InParallel.Flatten(...tasks) ⇒ CallbackTask

let InParallel = require('callback-patterns/InParallel');

let task = InParallel.Flatten(
  function(next, ...args) {},
  function(next, ...args) {},
  ...
);

task(next, ...args);
InParallel.Flatten is identical to InParallel, except tasks that return single results do not get autoboxed in arrays
note: because the callbacks can return any number of results, the results from each task are autoboxed into an array. This includes an empty array for tasks that don't return results.
Kind: static method of InParallel
Returns: CallbackTask - a wrapper function that runs the tasks in parallel
Params
  • ...tasks CallbackTask - any number of tasks to run in parallel.

Example
let InParallel = require('callback-patterns/InParallel');

let task = InParallel.Flatten(
  (next) => next(),
  (next) => next(null, 1),
  (next) => next(null, 2, 3),
);

let onDone = (err, ...results) => console.log(results);

chain(onDone); // prints [ undefined, 1, [ 2, 3 ] ]

callback-patterns.InSeries(...tasks) ⇒ CallbackTask

let InSeries = require('callback-patterns/InSeries');

let task = InSeries(
  function(next, ...args) {},
  function(next, ...args) {},
  ...
);

task(next, ...args);
Runs several tasks in series, and passes the results from one down to the next. This works similarly to the 'waterfall' method in caolan's async.
Kind: static method of callback-patterns
Returns: CallbackTask - a wrapper function that runs the tasks in series
Params
  • ...tasks CallbackTask - any number of tasks to run in series.

Example
let InSeries = require('callback-patterns/InSeries');

let chain = InSeries(
  (next) => { console.log(1); next();}
  InSeries(
    (next) => { console.log(2); next();}
    (next) => { console.log(3); next();}
  ),
  InSeries(
    (next) => { console.log(4); next();}
    (next) => { console.log(5); next();}
  )
)(); // prints out 1 2 3 4 5, eventually

callback-patterns.Logging(...statements) ⇒ CallbackTask

A logging utility. It passes the arguments received into all the statements, collects the results, and joins them together with newlines to build the final log statement
Kind: static method of callback-patterns
Returns: CallbackTask - a logging task
Params
  • ...statements function - any number of logging values. Functions are called with the calling arguments, everything else is passed directly to

Example
let InSeries = require('callback-patterns/InSeries');
let Logging = require('callback-patterns/Logging');

let task = InSeries(
  (next, ...args) => next(null, ...args),
  Logging(
    'log statement here'
    (...args) => `args are ${args}`
  ),
  (next, ...args) => next(null, ...args),
  ...
);

task(next, ...args);

callback-patterns.Memoize(CallbackTask, keyFunction, cache) ⇒ CallbackTask

Memoize builds a wrapper function that caches results of previous executions. As a result, repeated calls to Memoize may be much faster, if the request hits the cache.
NOTE: As of now, there are no cache eviction mechanisms. You should try to use Memoized functions in a 'disposable' way as a result
NOTE: Memoize is not 'thread-safe' currently. If two calls are made for the same object currently, two calls to the wrapped function will be made
NOTE: Memoize will cache errors as well as results.
Kind: static method of callback-patterns
Params
  • CallbackTask CallbackTask - the task function to memoize.
  • keyFunction function - a function that synchronously generates a key for a request.
  • cache object - a pre-filled cache to use

Example
let Delay = require('callback-patterns/Delay');
let InOrder = require('callback-patterns/InOrder');
let InSeries = require('callback-patterns/InSeries');
let Memoize = require('callback-patterns/Memoize');

let slowTask = InSeries(
  (next, i) => {
    console.log('task called with ', i);
    next(null, i + 1);
  },
  Delay(1000)
);

let memoizedTask = Memoize(slowTask);

let test = InOrder(
  memoizedTask,
  memoizedTask,
  memoizedTask
);


test(null, 1); // task is only called once, even though memoizedTask is called three times

callback-patterns.ParallelFilter(filter) ⇒ CallbackTask

Builds a task that filters all of its arguments in parallel, and returns the results
Kind: static method of callback-patterns
Returns: CallbackTask - a filtering task
Params
  • filter CallbackTask - an asynchronous filter function that returns true or false through its callback.

Example
let InSeries = require('callback-patterns/InSeries');
let Logging = require('callback-patterns/Logging');
let ParallelFilter = require('callback-patterns/ParallelFilter');

let isEven = (next, val) => next(null, val % 2 === 0);

let task = InSeries(
  (next) => next(null, 1, 2, 3, 4, 5, 6),
  Logging((...args) => args), // logs [1, 2, 3, 4, 5, 6]
  ParallelFilter(isEven),
  Logging((...args) => args), // logs [2, 4, 6]
  ...
);

task(next, ...args);

callback-patterns.ParallelMap(task) ⇒ CallbackTask

Builds a task wrapper that asynchronously maps each of its arguments to a result. Note: even though the mapping function can return any number of results, ParallelMap only uses the first result
Kind: static method of callback-patterns
Returns: CallbackTask - a parallel map task
Params
  • task CallbackTask - an asynchronous mapping function.

Example
let InSeries = require('callback-patterns/InSeries');
let Logging = require('callback-patterns/Logging');
let ParallelMap = require('callback-patterns/ParallelMap');

let addOne = (next, val) => next(null, val + 1);

let task = InSeries(
  (next) => next(null, 1, 2, 3, 4, 5, 6),
  Logging((...args) => args), // logs [1, 2, 3, 4, 5, 6]
  ParallelMap(addOne),
  Logging((...args) => args), // logs [2, 3, 4, 5, 6, 7]
  ...
);

task(next, ...args);

callback-patterns.PassThrough()

Sometimes, you need to pass previous arguments along with a new result. The easiest way to do this is to use PassThrough, which is a convenience method for:
(next, ...args) => next(null, ...args),

Kind: static method of callback-patterns
Example
let InSeries = require('callback-patterns/InSeries');
let Logging = require('callback-patterns/Logging');
let PassThrough = require('callback-patterns/PassThrough');

let task = InSeries(
  (next) => next(null, 1, 2, 3, 4, 5, 6),
			Logging((...args) => args), // logs [1, 2, 3, 4, 5, 6]
  PassThrough,
			Logging((...args) => args), // logs [2, 3, 4, 5, 6, 7]
  ...
);

task(next, ...args);

callback-patterns.Promisify(task) ⇒ function

Wraps around a task function and greates a promise generator, to make it easier to integrate task functions and promises.
NOTE: callback-patterns does not come bundled with a promise library, it expects Promise to already exists in the global namespace.
NOTE: if a function 'returns' multiple values through the next callback, Promisify auto-boxes these into an array.
Kind: static method of callback-patterns
Returns: function - a function that generates a Promise when called
Params
  • task function - a function that generates a promise from the args.

Example
let InSeries = require('callback-patterns/InSeries');
let Promisify = require('callback-patterns/Promisify');

let task = Promisify(
  InSeries(
    function(next, ...args) {...},
    function(next, ...args) {...},
    ...
  )
);

 task()
 .then()
 ...

callback-patterns.Race(...tasks) ⇒ CallbackTask

Race accepts a number of functions, and returns a task function that executes all of its child tasks simultaneously. The first result (or error) is returned, and the remaining results (or errors) are ignored.
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • ...tasks CallbackTask - any number of tasks to run in parallel.

Example
let Race = require('callback-patterns/Race');

let task = Race(
  (next) => next(null, 1),
  (next) => setTimeout(next, 100, null, 2),
  (next) => { throw new Error(); } ,
);

let onDone = (err, ...results) => console.log(results);

task(onDone); // prints out [ 1 ], eventually

callback-patterns.Retry(task, retryStrategy) ⇒ CallbackTask

Retry eraps a task and attempts to retry if it throws an error, with different kinds of retry strategies (manual, linear or exponential backoff)
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • task CallbackTask - the task to wrap.
  • retryStrategy function - an optional retry strategy.

Example
let Retry = require('callback-patterns/Retry');

let unstableTask = (next) => {
  if (Math.random() > 0.9) { throw new Error(); }
  else { next(); }
};

// attempt retries up to 10 times for up to 10000 ms ,
// with a constant retry delay of 100 ms between attempts
let stableTask = Retry(unstableTask, Retry.LinearRetryStrategy(100, 10, 10000));

// attempt retries up to 10 times for up to 10000 ms,
// with an exponential backoff retry delay of 100 ms, 200 ms, 400 ms, ...
let stableTask2 = Retry(unstableTask, Retry.ExponentialRetryStrategy(100, 10, 10000, 2));

// attempt retries up to 4 times,
// with retry delays of 100 ms, 100 ms, 100 ms, 1000ms
let stableTask3 = Retry(unstableTask, Retry.ManualRetryStrategy(100, 100, 100, 1000));

callback-patterns.Throttle(task, limit) ⇒ CallbackTask

Wraps a task and ensures that only X number of instances of the task can be run in parallel. Requests are queued up in an unbounded FIFO queue until they can be run.
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • task CallbackTask - the task to throttle
  • limit number - the number of instances that can run in parallel. default 1.

callback-patterns.TimeIn(task, ms) ⇒ CallbackTask

TimeIn wraps a single task function, and returns a function that only returns after X ms.
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • task CallbackTask - the task to wrap in a timeout.
  • ms number - the timein in ms.

Example
let TimeIn = require('callback-patterns/TimeIn');

let task = TimeIn(
  function(next, ...args) {},
			1000
);

task(next, ...args);

callback-patterns.TimeOut(task, ms) ⇒ CallbackTask

TimeOut wraps a single task function, and returns a function that returns early if the task fails to complete before the timeout triggers.
NOTE: the timeout being triggered will not cancel the original task.
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • task CallbackTask - the task to wrap in a timeout.
  • ms number - the timeout in ms.

Example
let TimeOut = require('callback-patterns/TimeOut');

let chain = TimeOut(
  function(next, ...args) {},
			1000
);

chain(next, ...args);

callback-patterns.Timer(task, label) ⇒ CallbackTask

Wraps a task and logs how long it takes to finish, or fail.
Kind: static method of callback-patterns
Returns: CallbackTask - a task
Params
  • task CallbackTask - the task to wrap.
  • label string - an optional label to log.

callback-patterns.While(conditionTask, loopTask) ⇒ function

While accepts two tasks and returns a task that conditionally executes some number of times.
Kind: static method of callback-patterns
Params
  • conditionTask function - a condition task.
  • loopTask function - a task to run if the condition returns a truthy value.

Example
let While = require('callback-patterns/While');

let task = While(
  (next, num) => next(null, num < 10),
  (next, num) => next(null, num + 1),
);

let onDone = (err, result) => console.log(result);

task(onDone, 1); // prints 9, eventually