README
async-patterns
async-patterns
is a collection of design patterns for async/await or promise-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.
API
object
async-patterns : A namespace.
Kind: global namespace
- async-patterns :
object
- .Callbackify ⇒
function
- .CatchError ⇒
function
- .InOrder ⇒
function
- .InParallel ⇒
function
- .InSeries ⇒
function
- .LogError ⇒
function
- .ParallelFilter ⇒
function
- .ParallelMap ⇒
function
- .PassThrough
- .Promisify ⇒
function
- .Race ⇒
function
- .testing :
object
- .AssertionTest
- new AssertionTest()
- assertionTest.describe(description) ⇒
AssertionTest
- assertionTest.setup(task) ⇒
AssertionTest
- assertionTest.prepare(task) ⇒
AssertionTest
- assertionTest.execute(task) ⇒
AssertionTest
- assertionTest.verify(...tasks) ⇒
AssertionTest
- assertionTest.teardown(task) ⇒
AssertionTest
- assertionTest.build() ⇒
function
- AssertionTest.VerifyErrorWasNotThrown()
- AssertionTest.VerifyErrorWasNotThrown()
- .AssertionTest
- .unstable :
object
- .TraceError(task) ⇒
function
- .TraceError(task) ⇒
- .Assert(validator, message) ⇒
taskFunction
- .Delay(delay) ⇒
taskFunction
- .If(ifTask, thenTask, elseTask) ⇒
taskFunction
- .Logging(...statements) ⇒
taskFunction
- .Memoize(task, [keyFunction]) ⇒
AsyncTask
- .Retry(task, options) ⇒
taskFunction
- .Throttle(task, limit) ⇒
taskFunction
- .TimeIn(task, ms) ⇒
taskFunction
- .TimeOut(task, ms) ⇒
taskFunction
- .Timer(task, label) ⇒
taskFunction
- .While(conditionTask, loopTask) ⇒
function
- .Callbackify ⇒
function
async-patterns.Callbackify ⇒ const task = Callbackify(
async (i) => i + 1
);
// logs 'res 1', eventually
task(
(err, res) => console.log('res', res),
0
);
Kind: static property of async-patterns
Returns: function
- a callback-expecting function
Params
- task
function
- an async function
function
async-patterns.CatchError ⇒ let task = CatchError(task);
const { error, result } = await task(request);
Kind: static property of async-patterns
Returns: function
- an async wrapper function around the task
Params
- task
function
- an async function to wrap around with a catch wrapper.
function
async-patterns.InOrder ⇒
let InOrder = require('async-patterns/InOrder');
const task = InOrder(
async (i) => i + 1,
async (i) => i + 1,
async (i) => i + 1
);
await task(0); // returns 3
Kind: static property of async-patterns
Returns: function
- an async wrapper function that runs all of the tasks in order, calling each one with original request
Params
- ...tasks
function
- any number of async tasks.
function
async-patterns.InParallel ⇒
let InParallel = require('async-patterns/InParallel');
const task = InParallel(
async (i) => i + 1,
async (i) => i + 2,
async (i) => i + 3
);
const results = await task(0); // results is [1, 2, 3]
Kind: static property of async-patterns
Returns: function
- an async wrapper function that runs all the tasks in parallel, and returns an array of results
Params
- ...tasks
function
- any number of async tasks.
function
async-patterns.InSeries ⇒
let InSeries = require('async-patterns/InSeries');
const task = InSeries(
async (i) => i + 1,
async (i) => i + 1,
async (i) => i + 1
);
const results = await task(0); // results is 3
Kind: static property of async-patterns
Returns: function
- an async wrapper function that runs all of the tasks in series, calling each one with the results of the previous one
Params
- ...tasks
function
- any number of async tasks.
function
async-patterns.LogError ⇒ let task = LogError(task);
// if an error occurs, it will be logged before getting re-thrown here
const result = await task(request);
Kind: static property of async-patterns
Returns: function
- an async wrapper function around the task
Params
- task
function
- an async function to wrap around with a error logging wrapper.
function
async-patterns.ParallelFilter ⇒ const task = ParallelFilter(
async (val, i) => val % 2 === 0
);
const results = await task([0, 1, 2]); // results is [0, 2]
Kind: static property of async-patterns
Returns: function
- an async wrapper function that takes in an array of requests, runs the task in parallel, once for each input in the array, and returns an array of results
Params
- task
function
- the filtering task
function
async-patterns.ParallelMap ⇒ const task = ParallelMap(
async (val, i) => val + 1
);
const results = await task([0, 1, 2]); // results is [1, 2, 3]
Kind: static property of async-patterns
Returns: function
- an async wrapper function that takes in an array of requests, runs the task in parallel, once for each input in the array, and returns an array of results
Params
- task
function
- the mapping task
async-patterns.PassThrough
const task = PassThrough;
const results = await task(0); // results is 0
PassThrough does nothing, just passes the request through as the result
Kind: static property of async-patterns
function
async-patterns.Promisify ⇒ const task = Promisify(
(onDone, i) => onDone(
i === 0 ? new Error('i cant be 0') : null,
i + 1
),
);
const results = await task(1); // results is 2
const results2 = await taks(0); // throws 'i cant be 0 Error
Kind: static property of async-patterns
Returns: function
- an async function
Params
- task
function
- a callback-expecting function
function
async-patterns.Race ⇒ const task = Race(
async (i) => i + 1,
async (i) => i + 2,
);
const result = await task(1); // 2
Kind: static property of async-patterns
Returns: function
- an async task that resolves or rejects as soon as the first one of its "children" resolves or rejects
Params
- ...tasks
function
- any number of async tasks
object
async-patterns.testing : A namespace.
Kind: static namespace of async-patterns
- .testing :
object
- .AssertionTest
- new AssertionTest()
- assertionTest.describe(description) ⇒
AssertionTest
- assertionTest.setup(task) ⇒
AssertionTest
- assertionTest.prepare(task) ⇒
AssertionTest
- assertionTest.execute(task) ⇒
AssertionTest
- assertionTest.verify(...tasks) ⇒
AssertionTest
- assertionTest.teardown(task) ⇒
AssertionTest
- assertionTest.build() ⇒
function
- AssertionTest.VerifyErrorWasNotThrown()
- AssertionTest.VerifyErrorWasNotThrown()
- .AssertionTest
testing.AssertionTest
Kind: static class of testing
- .AssertionTest
- new AssertionTest()
- assertionTest.describe(description) ⇒
AssertionTest
- assertionTest.setup(task) ⇒
AssertionTest
- assertionTest.prepare(task) ⇒
AssertionTest
- assertionTest.execute(task) ⇒
AssertionTest
- assertionTest.verify(...tasks) ⇒
AssertionTest
- assertionTest.teardown(task) ⇒
AssertionTest
- assertionTest.build() ⇒
function
- AssertionTest.VerifyErrorWasNotThrown()
- 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
assertionTest.describe(description) ⇒ 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
assertionTest.setup(task) ⇒ 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
assertionTest.prepare(task) ⇒ 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
assertionTest.execute(task) ⇒ 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
assertionTest.verify(...tasks) ⇒ 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
assertionTest.teardown(task) ⇒ 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
function
assertionTest.build() ⇒ 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
object
async-patterns.unstable : A namespace.
Kind: static namespace of async-patterns
function
unstable.TraceError(task) ⇒ 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: function
- a wrapper function that modifies the stack trace of any errors thrown within
Params
- task
function
- a task function to wrap
taskFunction
async-patterns.Assert(validator, message) ⇒ let Assert = require('async-patterns/Assert');
let InSeries = require('async-patterns/InSeries');
let task = InSeries(
(num) => num,
Assert(
(num) => (num >= 0),
(num) => `${num} is less than zero`
),
(num) => num,
);
await task(1); // returns 1
await task(-1); // throws error
Builds an async assertion task.
Kind: static method of async-patterns
Returns: taskFunction
- 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.
taskFunction
async-patterns.Delay(delay) ⇒ let Delay = require('async-patterns/Delay');
let InSeries = require('async-patterns/InSeries');
let task = InSeries(
(num) => num + 1
Delay(100),
);
await task(1); // returns 2, after a 100ms delay
Delay acts like PassThrough, but inserts a delay in the call.
Kind: static method of async-patterns
Returns: taskFunction
- a delay task
Params
- delay
number
- The time to delay, in ms.
taskFunction
async-patterns.If(ifTask, thenTask, elseTask) ⇒ let If = require('async-patterns/If');
let logIfEven = If(
(num) => (num % 2 === 0),
(num) => { console.log('is even!'); },
(num) => { console.log('is not even!'); }
);
await logIfEven(1); // prints out 'is not even!' eventually
await logIfEven(2); // prints out 'is even!' eventually
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 async-patterns
Params
- ifTask
taskFunction
- a condition task. - thenTask
taskFunction
- a task to run when ifTask returns a truthy value. - elseTask
taskFunction
- a task to run when ifTask returns a falsy value.
taskFunction
async-patterns.Logging(...statements) ⇒ 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 async-patterns
Returns: taskFunction
- a logging task
Params
- ...statements
function
- any number of logging values. Functions are called with the calling arguments, everything else is passed directly to
AsyncTask
async-patterns.Memoize(task, [keyFunction]) ⇒ 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 async-patterns
Params
- task
AsyncTask
- the task function to memoize. - [keyFunction]
function
- a function that synchronously generates a key for a request.
taskFunction
async-patterns.Retry(task, options) ⇒ Wraps a task and attempts to retry if it throws an error, with an exponential backoff.
Kind: static method of async-patterns
Returns: taskFunction
- a task
Params
- task
taskFunction
- the task to wrap. - options
object
- an optional set of retry options.- .timeout
object
- maximum time to attempt retries. - .retries
object
- maximum number of retries to attempt.
- .timeout
taskFunction
async-patterns.Throttle(task, limit) ⇒ 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 async-patterns
Returns: taskFunction
- a task
Params
- task
taskFunction
- the task to throttle - limit
number
- the number of instances that can run in parallel. default 1.
taskFunction
async-patterns.TimeIn(task, ms) ⇒
let TimeIn = require('async-patterns/TimeIn');
let task = TimeIn(
async function (...args) {},
1000
);
await task(...args);
TimeIn wraps a single task function, and returns a function that only returns after X ms.
Kind: static method of async-patterns
Returns: taskFunction
- a task
Params
- task
taskFunction
- the task to wrap in a timeout. - ms
number
- the timein in ms.
taskFunction
async-patterns.TimeOut(task, ms) ⇒
let TimeOut = require('async-patterns/TimeOut');
let task = TimeOut(
async function (...args) {},
1000
);
await task(...args);
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 async-patterns
Returns: taskFunction
- a task
Params
- task
taskFunction
- the task to wrap in a timeout. - ms
number
- the timeout in ms.
taskFunction
async-patterns.Timer(task, label) ⇒ Wraps a task and logs how long it takes to finish, or fail.
Kind: static method of async-patterns
Returns: taskFunction
- a task
Params
- task
taskFunction
- the task to wrap. - label
string
- an optional label to log.
function
async-patterns.While(conditionTask, loopTask) ⇒
let While = require('async-patterns/While');
let task = While(
(num) => (num < 10),
(num) => num + 1
);
await task(1); // prints 10, eventually
While accepts two tasks and returns a task that conditionally executes some number of times.
Kind: static method of async-patterns
Params
- conditionTask
function
- a condition task. - loopTask
function
- a task to run if the condition returns a truthy value.