[{%= name %}][author-www-url] [![npmjs.com][npmjs-img]][npmjs-url] [![The MIT License][license-img]][license-url] [![npm downloads][downloads-img]][downloads-url]
{%= description %}
[![code climate][codeclimate-img]][codeclimate-url] [![travis build status][travis-img]][travis-url] [![coverage status][coveralls-img]][coveralls-url] [![dependency status][david-img]][david-url]
Install with npm
$ npm i {%= name %} --save
For more use-cases see the tests
const fs = require('fs')
const {%= varname %} = require('{%= name %}')
{%= varname %}((cb) => {
fs.readFile('./package.json', 'utf8', cb)
}, (err, res) => {
if (err) return console.error(err)
let json = JSON.parse(res)
console.log(json.name) // => '{%= name %}'
})
Behind the scenes we use just good plain old try/catch
block. Sounds you strange? See what "hard" job is done on [try-catch-callback][] and [try-catch-core][].
In the first one, we just calls a function inside try/catch and calls done
callback with error or result of that function.
About second one, there we wraps the done
callback with [once][] and [dezalgo][] to ensure it will be called in the next tick.
Here, in always-done
, we just give a callback
to that [try-catch-core][] package and "listen" what is the result. Actually we not listening anything, we just make a few checks to understand what the incoming value is - promise, child process, stream, observable and etc.
Nothing so magical. Try/catch block for most of the things works briliant. And [on-stream-end][] module (which is drop-in replacement for [end-of-stream][]) for streams and child processes.
{%= apidocs('index.js') %}
Handles completion and errors of async/await, synchronous and asynchronous (callback) functions, also functions that returns streams, promises, child process and observables.
alwaysDone(async function () {
return await Promise.resolve('foobar')
}, function done (e, res) {
console.log(res) // => 'foobar'
})
var alwaysDone = require('always-done')
alwaysDone(function (cb) {
fs.readFile('./package.json', 'utf8', cb)
}, function done (err, res) {
if (err) return console.log(err)
var pkg = JSON.parse(res)
console.log(pkg.name) // => 'always-done'
})
alwaysDone(function () {
return 123
}, function done (e, res) {
console.log(res) // => 123
})
alwaysDone(function () {
return new Error('qux bar')
}, function done (err) {
console.log(err.message) // => 'qux bar'
})
alwaysDone(function () {
return Promise.resolve(12345)
}, function done (e, res) {
console.log(res) // => 12345
})
alwaysDone(function () {
return Promise.reject(new Error('foo bar'))
}, function done (err) {
console.log(err.message) // => 'foo bar
})
Using [on-stream-end][] and [stream-exhaust][]
alwaysDone(function () {
return fs.createReadStream('./package.json')
}, function done (e) {
console.log('stream completed')
})
alwaysDone(function () {
return fs.createReadStream('foo bar')
}, function done (err) {
console.log(err.code) // => ENOENT
console.log(err.message) // => No such file or directory
})
alwaysDone(function () {
var read = fs.createReadStream('foo bar')
return read.pipe(through2())
}, function done (err) {
console.log(err.code) // => ENOENT
console.log(err.message) // => No such file or directory
})
Using
.subscribe
method of the observable
var Observable = require('rx').Observable
alwaysDone(function () {
return Observable.empty()
}, function done (e, res) {
console.log(e, res) // => null, undefined
})
alwaysDone(function () {
return Observable.return([1, 2, 3])
}, function done (e, res) {
console.log(res) // => [1, 2, 3]
})
alwaysDone(function () {
return Observable.throw(new Error('observable error'))
}, function done (err) {
console.log(err.message) // => 'observable error'
})
Basically, they are streams, so completion is handled using [on-stream-end][] which is drop-in replacement for [end-of-stream][]
var cp = require('child_process')
alwaysDone(function () {
return cp.exec('echo hello world')
}, function done (e, res) {
console.log(res) // => 'hello world'
})
var cp = require('child_process')
alwaysDone(function () {
return cp.exec('foo-bar-baz sasa')
}, function done (err) {
console.log(err.message) // => 'exited with error code: 12'
})
var cp = require('child_process')
alwaysDone(function () {
return cp.spawn('foo-bar-baz', ['hello world'])
}, function done (err) {
console.log(err.code) // => ENOENT
})
alwaysDone(function () {
foo // ReferenceError
return 55
}, function (err) {
console.log(err.name)
// => ReferenceError: foo is not defined
})
alwaysDone(function () {
JSON.parse('{"foo":')
}, function (err) {
console.log(err)
// => SyntaxError: Unexpected end of JSON input
})
It may looks strange, but it's logical. If you pass empty function it just completes with
undefined
result andnull
error.
Example
// passing empty function
alwaysDone(function () {}, function (err, res) {
console.log(err, res) // => null, undefined
})
var {%= varname %} = require('{%= name %}')
var opts = {
context: { foo: 'bar' }
}
{%= varname %}(function () {
console.log(this.foo) // => 'bar'
}, opts, function done () {
console.log('end')
})
It may be strange, but this allows you to pass more arguments to that first function and the last argument always will be "callback" until
fn
is async or sync but withpassCallback: true
option.
var {%= varname %} = require('{%= name %}')
var options = {
args: [1, 2]
}
{%= varname %}(function (a, b) {
console.log(arguments.length) // => 2
console.log(a) // => 1
console.log(b) // => 2
return a + b + 3
}, options, function done (e, res) {
console.log(res) // => 9
})
Can be used as thunkify lib without problems, just don't pass a done callback.
var fs = require('fs')
var {%= varname %} = require('{%= name %}')
var readFileThunk = {%= varname %}(function (cb) {
fs.readFile('./package.json', cb)
})
readFileThunk(function done (err, res) {
console.log(err, res) // => null, Buffer
})
back to top {% if (verb.related && verb.related.list && verb.related.list.length) { %}
{%= related(verb.related.list, {words: 12}) %} {% } %}
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= repository %}/issues/new).
But before doing anything, please read the CONTRIBUTING.md guidelines.
Charlike Make Reagent [![new message to charlike][new-message-img]][new-message-url]
[![{%= author.username %}.tk][author-www-img]][author-www-url] [![keybase {%= author.username %}][keybase-img]][keybase-url] [![{%= author.username %} npm][author-npm-img]][author-npm-url] [![{%= author.username %} twitter][author-twitter-img]][author-twitter-url] [![{%= author.username %} github][author-github-img]][author-github-url]
{%= reflinks(verb.reflinks) %}
[npmjs-url]: https://www.npmjs.com/package/{%= name %} [npmjs-img]: https://img.shields.io/npm/v/{%= name %}.svg?label={%= name %}
[license-url]: https://github.com/{%= repository %}/blob/master/LICENSE [license-img]: https://img.shields.io/npm/l/{%= name %}.svg
[downloads-url]: https://www.npmjs.com/package/{%= name %} [downloads-img]: https://img.shields.io/npm/dm/{%= name %}.svg
[codeclimate-url]: https://codeclimate.com/github/{%= repository %} [codeclimate-img]: https://img.shields.io/codeclimate/github/{%= repository %}.svg
[travis-url]: https://travis-ci.org/{%= repository %} [travis-img]: https://img.shields.io/travis/{%= repository %}/master.svg
[coveralls-url]: https://coveralls.io/r/{%= repository %} [coveralls-img]: https://img.shields.io/coveralls/{%= repository %}.svg
[david-url]: https://david-dm.org/{%= repository %} [david-img]: https://img.shields.io/david/{%= repository %}.svg
[author-www-url]: http://www.{%= author.username.toLowerCase() %}.tk [author-www-img]: https://img.shields.io/badge/www-{%= author.username.toLowerCase() %}.tk-fe7d37.svg
[keybase-url]: https://keybase.io/{%= author.username.toLowerCase() %} [keybase-img]: https://img.shields.io/badge/keybase-{%= author.username.toLowerCase() %}-8a7967.svg
[author-npm-url]: https://www.npmjs.com/~{%= author.username.toLowerCase() %} [author-npm-img]: https://img.shields.io/badge/npm-~{%= author.username.toLowerCase() %}-cb3837.svg
[author-twitter-url]: https://twitter.com/{%= author.username %} [author-twitter-img]: https://img.shields.io/badge/twitter-@{%= author.username %}-55acee.svg
[author-github-url]: https://github.com/{%= author.username %} [author-github-img]: https://img.shields.io/badge/github-@{%= author.username %}-4183c4.svg
[new-message-url]: https://github.com/{%= author.username %}/ama [new-message-img]: https://img.shields.io/badge/ask%20me-anything-green.svg