262 lines
8.5 KiB
Markdown
262 lines
8.5 KiB
Markdown
<p align="center">
|
|
<a href="https://gulpjs.com">
|
|
<img height="257" width="114" src="https://raw.githubusercontent.com/gulpjs/artwork/master/gulp-2x.png">
|
|
</a>
|
|
</p>
|
|
|
|
# bach
|
|
|
|
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url]
|
|
|
|
Compose your async functions with elegance.
|
|
|
|
## Usage
|
|
|
|
With `bach`, it is very easy to compose async functions to run in series or parallel.
|
|
|
|
```js
|
|
var bach = require('bach');
|
|
|
|
function fn1(cb) {
|
|
cb(null, 1);
|
|
}
|
|
|
|
function fn2(cb) {
|
|
cb(null, 2);
|
|
}
|
|
|
|
function fn3(cb) {
|
|
cb(null, 3);
|
|
}
|
|
|
|
var seriesFn = bach.series(fn1, fn2, fn3);
|
|
// fn1, fn2, and fn3 will be run in series
|
|
seriesFn(function (err, res) {
|
|
if (err) {
|
|
// in this example, err is undefined
|
|
// handle error
|
|
}
|
|
// handle results
|
|
// in this example, res is [1, 2, 3]
|
|
});
|
|
|
|
var parallelFn = bach.parallel(fn1, fn2, fn3);
|
|
// fn1, fn2, and fn3 will be run in parallel
|
|
parallelFn(function (err, res) {
|
|
if (err) {
|
|
// in this example, err is undefined
|
|
// handle error
|
|
}
|
|
// handle results
|
|
// in this example, res is [1, 2, 3]
|
|
});
|
|
```
|
|
|
|
Since the composer functions return a function, you can combine them.
|
|
|
|
```js
|
|
var combinedFn = bach.series(fn1, bach.parallel(fn2, fn3));
|
|
// fn1 will be executed before fn2 and fn3 are run in parallel
|
|
combinedFn(function (err, res) {
|
|
if (err) {
|
|
// in this example, err is undefined
|
|
// handle error
|
|
}
|
|
// handle results
|
|
// in this example, res is [1, [2, 3]]
|
|
});
|
|
```
|
|
|
|
Functions are called with [async-done], so you can return a stream, promise, observable or child process. See [`async-done` completion and error resolution][completions] for more detail.
|
|
|
|
```js
|
|
// streams
|
|
var fs = require('fs');
|
|
|
|
function streamFn1() {
|
|
return fs
|
|
.createReadStream('./example')
|
|
.pipe(fs.createWriteStream('./example'));
|
|
}
|
|
|
|
function streamFn2() {
|
|
return fs
|
|
.createReadStream('./example')
|
|
.pipe(fs.createWriteStream('./example'));
|
|
}
|
|
|
|
var parallelStreams = bach.parallel(streamFn1, streamFn2);
|
|
parallelStreams(function (err) {
|
|
if (err) {
|
|
// in this example, err is undefined
|
|
// handle error
|
|
}
|
|
// all streams have emitted an 'end' or 'close' event
|
|
});
|
|
```
|
|
|
|
```js
|
|
// promises
|
|
function promiseFn1() {
|
|
return Promise.resolve(1);
|
|
}
|
|
|
|
function promiseFn2() {
|
|
return Promise.resolve(2);
|
|
}
|
|
|
|
var parallelPromises = bach.parallel(promiseFn1, promiseFn2);
|
|
parallelPromises(function (err, res) {
|
|
if (err) {
|
|
// in this example, err is undefined
|
|
// handle error
|
|
}
|
|
// handle results
|
|
// in this example, res is [1, 2]
|
|
});
|
|
```
|
|
|
|
All errors are caught in a [domain] and passed to the final callback as the first argument.
|
|
|
|
```js
|
|
function success(cb) {
|
|
setTimeout(function () {
|
|
cb(null, 1);
|
|
}, 500);
|
|
}
|
|
|
|
function error() {
|
|
throw new Error('Thrown Error');
|
|
}
|
|
|
|
var errorThrownFn = bach.parallel(error, success);
|
|
errorThrownFn(function (err, res) {
|
|
if (err) {
|
|
// handle error
|
|
// in this example, err is an error caught by the domain
|
|
}
|
|
// handle results
|
|
// in this example, res is [undefined]
|
|
});
|
|
```
|
|
|
|
When an error happens in a parallel composition, the callback will be called as soon as the error happens.
|
|
If you want to continue on error and wait until all functions have finished before calling the callback, use `settleSeries` or `settleParallel`.
|
|
|
|
```js
|
|
function success(cb) {
|
|
setTimeout(function () {
|
|
cb(null, 1);
|
|
}, 500);
|
|
}
|
|
|
|
function error(cb) {
|
|
cb(new Error('Async Error'));
|
|
}
|
|
|
|
var parallelSettlingFn = bach.settleParallel(success, error);
|
|
parallelSettlingFn(function (err, res) {
|
|
// all functions have finished executing
|
|
if (err) {
|
|
// handle error
|
|
// in this example, err is an error passed to the callback
|
|
}
|
|
// handle results
|
|
// in this example, res is [1]
|
|
});
|
|
```
|
|
|
|
## API
|
|
|
|
### `series(fns..., [options])`
|
|
|
|
Takes a variable amount of functions (`fns`) to be called in series when the returned function is
|
|
called. Optionally, takes an [options](#options) object as the last argument.
|
|
|
|
Returns an `invoker(cb)` function to be called to start the serial execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.
|
|
|
|
If all functions complete successfully, the callback function will be called with all `results` as the second argument.
|
|
|
|
If an error occurs, execution will stop and the error will be passed to the callback function as the first parameter. The error parameter will always be a single error.
|
|
|
|
### `parallel(fns..., [options])`
|
|
|
|
Takes a variable amount of functions (`fns`) to be called in parallel when the returned function is
|
|
called. Optionally, takes an [options](#options) object as the last argument.
|
|
|
|
Returns an `invoker(cb)` function to be called to start the parallel execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.
|
|
|
|
If all functions complete successfully, the callback function will be called with all `results` as the second argument.
|
|
|
|
If an error occurs, the callback function will be called with the error as the first parameter. Any async functions that have not completed, will still complete, but their results will **not** be available. The error parameter will always be a single error.
|
|
|
|
### `settleSeries(fns..., [options])`
|
|
|
|
Takes a variable amount of functions (`fns`) to be called in series when the returned function is
|
|
called. Optionally, takes an [options](#options) object as the last argument.
|
|
|
|
Returns an `invoker(cb)` function to be called to start the serial execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.
|
|
|
|
All functions will always be called and the callback will receive all settled errors and results. If any errors occur, the error parameter will be an array of errors.
|
|
|
|
### `settleParallel(fns..., [options])`
|
|
|
|
Takes a variable amount of functions (`fns`) to be called in parallel when the returned function is
|
|
called. Optionally, takes an [options](#options) object as the last argument.
|
|
|
|
Returns an `invoker(cb)` function to be called to start the parallel execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.
|
|
|
|
All functions will always be called and the callback will receive all settled errors and results. If any errors occur, the error parameter will be an array of errors.
|
|
|
|
### `options`
|
|
|
|
The `options` object is primarily used for specifying functions that give insight into the lifecycle of each function call. The possible extension points are `create`, `before`, `after` and `error`. If an extension point is not specified, it defaults to a no-op function.
|
|
|
|
The `options` object for `parallel` and `settleParallel` also allows specifying `concurrency` in which to run your functions. By default, your functions will run at maximum concurrency.
|
|
|
|
##### `options.concurrency`
|
|
|
|
Limits the amount of functions allowed to run at a given time.
|
|
|
|
##### `options.create(fn, index)`
|
|
|
|
Called at the very beginning of each function call with the function (`fn`) being executed and the `index` from the array/arguments. If `create` returns a value (`storage`), it is passed to the `before`, `after` and `error` extension points.
|
|
|
|
If a value is not returned, an empty object is used as `storage` for each other extension point.
|
|
|
|
This is useful for tracking information across an iteration.
|
|
|
|
##### `options.before(storage)`
|
|
|
|
Called immediately before each function call with the `storage` value returned from the `create` extension point.
|
|
|
|
##### `options.after(result, storage)`
|
|
|
|
Called immediately after each function call with the `result` of the function and the `storage` value returned from the `create` extension point.
|
|
|
|
##### `options.error(error, storage)`
|
|
|
|
Called immediately after a failed function call with the `error` of the function and the `storage` value returned from the `create` extension point.
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
<!-- prettier-ignore-start -->
|
|
[downloads-image]: https://img.shields.io/npm/dm/bach.svg?style=flat-square
|
|
[npm-url]: https://www.npmjs.com/package/bach
|
|
[npm-image]: https://img.shields.io/npm/v/bach.svg?style=flat-square
|
|
|
|
[ci-url]: https://github.com/gulpjs/bach/actions?query=workflow:dev
|
|
[ci-image]: https://img.shields.io/github/workflow/status/gulpjs/bach/dev?style=flat-square
|
|
|
|
[coveralls-url]: https://coveralls.io/r/gulpjs/bach
|
|
[coveralls-image]: https://img.shields.io/coveralls/gulpjs/bach.svg?style=flat-square
|
|
<!-- prettier-ignore-end -->
|
|
|
|
<!-- prettier-ignore-start -->
|
|
[domain]: https://nodejs.org/api/domain.html
|
|
[async-done]: https://github.com/gulpjs/async-done
|
|
[completions]: https://github.com/gulpjs/async-done#completion-and-error-resolution
|
|
<!-- prettier-ignore-end -->
|