autoesc
Version:
Error Short Circuiter (ESC) function decorator
143 lines (86 loc) • 4.03 kB
Markdown
# Error Short Circuiter (ESC) for IcedCoffeeScript's autocb
Provides a very concise implementaiton of ESC to be used with IcedCoffeeScript's autocb feature.
Design goals:
* minimal code noise
* safe where possible
Features:
* designed for autocb functions
* minimal typing (just prepend “autocb” to “defer” and boom, all errors are handled)
* fails fast (no way to make hard-to-find error handling mistakes)
* catches errors (wraps the function in a try-catch block, sends all errors to the callback)
* guarantees that the callback is only called once, even if multiple errors occur
See below for a detailed explanation of this package and Error Short Circuiters in general.
Installation:
npm install autoesc
## How do you handle errors in IcedCoffeeScript?
### 1. Manually
Consider the following function that reads a file, parses it and does some processing:
myAsyncFunc1 = (path, callback) ->
await fs.readFile(path, 'utf8', defer(err, raw))
if err then return callback(err)
try
data = JSON.parse(raw)
catch e
return callback(e)
await Image.findById(data.imageId, defer(err, image))
if err then return callback(err)
await checkPermissions(globalUser, image, defer(err, permitted))
if err then return callback(err)
if !permitted
return callback(new Error('access denied'))
callback null, image
### 2. With errTo
It's a mess, and a popular way to deal with it is to use an [Error Short Circuiter (ESC)](http://stackoverflow.com/a/16654079).
One good implementation is [errTo](https://github.com/ashtuchkin/errTo), resulting in the following code:
errTo = require('errto')
myAsyncFunc2 = (path, callback) ->
await fs.readFile(path, 'utf8', errTo(callback, defer raw))
try
data = JSON.parse(raw)
catch e
return callback(e)
await Image.findById(data.imageId, errTo(callback, defer image))
await checkPermissions(globalUser, image, errTo(callback, defer permitted))
if !permitted
return callback(new Error('access denied'))
callback null, image
### 3. With bound errTo
That's much better, but you can make the code more concise by binding errTo:
errTo = require('errto')
myAsyncFunc3 = (path, callback) ->
esc = errTo.bind(null, callback)
await fs.readFile(path, 'utf8', esc defer raw)
try
data = JSON.parse(raw)
catch e
return callback(e)
await Image.findById(data.imageId, esc defer image)
await checkPermissions(globalUser, image, esc defer permitted)
if !permitted
return callback(new Error('access denied'))
callback null, image
### 4. With autoesc
This package allows you to write:
esc = require('autoesc')
myAsyncFunc4 = esc (path, autocb) ->
await fs.readFile(path, 'utf8', autocb defer raw)
data = JSON.parse(raw)
await Image.findById(data.imageId, autocb defer image)
await checkPermissions(globalUser, image, autocb defer permitted)
if !permitted
throw new Error('access denied')
return image
Now, this is not for everyone; some may feel there's too much magic involved. Also:
* You cannot return successful results of type Error
* You cannot return functions as successful results
The advantage is that there's no way to screw up callbacks and error handling with autocb + autoesc:
* If you forget to write “esc” or “autocb defer”, the function will fail in every case, so you'll catch the error quickly.
* All errors (including runtime exceptions thrown by the function itself) will be handled, whatever you do.
* The callback will be called exactly once, whatever you do.
## Recipe
1. `esc = require('autoesc')`
2. Decorate your async functions with `esc`.
3. Use `autocb` to let IcedCoffeeScript invoke callbacks for you.
4. Prepend `autocb` to `defer` to automatically handle errors.
## License
Copyright 2014, Andrey Tarantsov. Licensed under MIT.