letta

Promisify sync, async or generator function, using relike. Kind of promisify, but lower level. Full compatibility with co4 and passing 100% of its tests.
You might also be interested in relike, relike-all, relike-value and letta-value.
Table Of Contents
(TOC generated by verb using markdown-toc)
Highlights
A few features and main points.
- promisify sync, async and generator functions
- lower lever than "promisify" - giving function to 1st argument, the next arguments are passed to it
- thin wrapper around relike package to add support for generators, using co
- full compatibility with
co@4and passing 100% of its tests - correct detecting async (callback-style) functions
- correct handling of optional arguments, just
fn.lengthnot works - believe - great handling of errors, uncaught exceptions, rejections and optional arguments
- never crash, all is silent, listening on
unhandledRejectionanduncaughtException - always stay to the standards and specs
- always use native Promise, you can't trick that
- only using Bluebird, if not other Promise constructor provided through
.Promiseproperty - Bluebird or the custom constructor is used only on enviroments that don't have support for native Promise
- works on any nodejs version - from
v0.10.xto latestv6+Node.js - accept and works with javascript internal functions like
JSON.stringifyandJSON.parse
Notes
Sync and async functions
Note that it treats functions as asynchronous, based on is-async-function.
Why you should be aware of that? Because if you give async function which don't have last argument called with some of the common-callback-names it will treat that function as synchronous and things may not work as expected.
It's not a problem for most of the cases and for node's native packages, because that's a convention.
Absolutely silent - never crash
Using letta you should be absolutely careful. Because it makes your application absolutely silent. Which means
if you have some ReferenceError or something like it, after the execution of letta it will be muted. And the
only way to handle it is through .catch from the returned promise.
Let's visualise it. In the following examples we'll use relike first and then letta, and you can see the differences.
var relike = require('relike')
var promise = relike(function () {
return 123
})
promise.then(console.log, err => {
console.error(err.stack)
// => errors only happened in function wrapped by relike
})
foo
// => throws ReferenceError directly, immediately
// and your application will crashBut the things, using letta are little bit different, because we have listeners on unhandledRejection and
on uncaughtException events. The same example from above, using letta
var letta = require('letta')
var promise = letta(function () {
return 123
})
promise.then(console.log, err => {
console.error(err.stack)
// => ReferenceError: foo is not defined
})
foo
// => never throws directly, never crash
// this error should be handled from the promiseSo, if you don't want this behavior, you should use relike. But if you want generators support, you should do some little wrapper for relike.
Install
npm i letta --saveUsage
For more use-cases see the tests, examples or all the passing
co@4tests
const fs = require('fs')
const letta = require('letta')
const promise = letta(function * () {
let result = yield Promise.resolve(123)
return result
})
promise.then(value => {
console.log(value) // => 123
}, err => {
console.error(err.stack)
})If you want to convert generator function to regular function that returns a Promise use letta.promisify
Example
const letta = require('letta')
const fn = letta.promisify(function * (number) {
return yield Promise.resolve(number)
})
fn(456).then(number => {
console.log(number) // => 456
}, err => {
console.error(err.stack)
})If you want to promisify any type of function, again, just use the .promisify method, like you do with bluebird.promisify.
const fs = require('fs')
const letta = require('letta')
const readFile = letta.promisify(fs.readFile)
readFile('package.json', 'utf8')
.then(JSON.parse)
.then(value => {
console.log(value.name) // => 'letta'
})
.catch(SyntaxError, err => {
console.error('File had syntax error', err)
})
// Catch any other error
.catch(err => {
console.error(err.stack)
})API
letta
Control flow for now and then.
Params
<fn>{Function}: Regular function (including arrow function) or generator function.[...args]{Mixed}: Any number of any type of arguments, they are passed tofn.returns{Promise}: Always native Promise if supported on enviroment.
Example
const letta = require('letta')
letta((foo, bar, baz) => {
console.log(foo, bar, baz) // => 'foo bar baz'
return foo
}, 'foo', 'bar', 'baz')
.then(console.log) // => 'foo'.promisify
Returns a function that will wrap the given
fn. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the givenfnnode function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. – Bluebird Docs on.promisify
Params
<fn>{Function}: Regular function (including arrow function) or generator function.[Promize]{Function}: Promise constructor to be used on enviroment where no support for native.returns{Function}: Promisified function, which always return a Promise when called.
Example
const fs = require('fs')
const letta = require('letta')
const readFile = letta.promisify(fs.readFile)
readFile('package.json', 'utf8')
.then(JSON.parse)
.then(value => {
console.log(value.name) // => 'letta'
})
.catch(SyntaxError, err => {
console.error('File had syntax error', err)
})
// Catch any other error
.catch(err => {
console.error(err.stack)
})
// or promisify generator function
const promise = letta(function * () {
let result = yield Promise.resolve(123)
return result
})
promise.then(value => {
console.log(value) // => 123
}, err => {
console.error(err.stack)
}).Promise
While letta always trying to use native Promise if available in the enviroment, you can
give a Promise constructor to be used on enviroment where there's no support - for example, old
broswers or node's 0.10 version. By default, letta will use and include bluebird on old enviroments,
as it is the fastest implementation of Promises. So, you are able to give Promise constructor, but
it won't be used in modern enviroments - it always will use native Promise, you can't trick that. You
can't give custom promise implementation to be used in any enviroment.
Example
var fs = require('fs')
var letta = require('letta')
letta.Promise = require('q') // using `Q` promise on node 0.10
var readFile = letta.promisify(fs.readFile)
readFile('package.json', 'utf8')
.then(console.log, err => {
console.error(err.stack)
})One way to pass a custom Promise constructor is as shown above. But the other way is passing it to .Promise of the promisified function, like that
var fs = require('fs')
var letta = require('letta')
var statFile = letta.promisify(fs.stat)
statFile.Promise = require('when') // using `when` promise on node 0.10
statFile('package.json').then(console.log, console.error)One more thing, is that you can access the used Promise and can detect what promise is used. It is easy, just as promise.Promise and you'll get it.
Or look for promise.___bluebirdPromise and promise.___customPromise properties. .___bluebirdPromise (yea, with three underscores in front) will be true if enviroment is old and you didn't provide promise constructor to .Promise.
So, when you give constructor .__customPromise will be true and .___bluebirdPromise will be false.
var fs = require('fs')
var letta = require('letta')
var promise = letta(fs.readFile, 'package.json', 'utf8')
promise.then(JSON.parse).then(function (val) {
console.log(val.name) // => 'letta'
}, console.error)
console.log(promise.Promise) // => used Promise constructor
console.log(promise.___bluebirdPromise) // => `true` on old env, falsey otherwise
console.log(promise.___customPromise) // => `true` when pass `.Promise`, falsey otherwiseExamples
Few working examples with what can be passed and how
lettaacts.
- Callback functions
- Generator functions
- JSON.stringify
- Synchronous functions
- Exceptions and rejections
- Returning errors
- Passing function as last argument
Callback functions
Can accept asynchronous (callback) functions as well.
Example
const fs = require('fs')
const letta = require('letta')
letta(fs.readFile, 'package.json', 'utf8')
.then(JSON.parse)
.then(data => {
console.log(data.name) // => 'letta'
}, err => {
console.error(err.stack)
})
// callback `fs.stat` function
letta(fs.stat, 'package.json')
.then(stats => {
console.log(stats.isFile()) // => true
}, err => {
console.error(err.stack)
})Generator functions
Accept generator functions same as
coand acts likeco@4.
Example
const fs = require('fs')
const letta = require('letta')
letta(function * (filepath) {
return yield letta(fs.readFile, filepath, 'utf8')
}, 'package.json')
.then(JSON.parse)
.then(data => {
console.log(data.name) // => 'letta'
}, err => {
console.error(err.stack)
})JSON.stringify
Specific use-case which shows correct handling of optional arguments.
const letta = require('letta')
letta(JSON.stringify, { foo: 'bar' })
.then(data => {
console.log(data) // => {"foo":"bar"}
}, console.error)
// result with identation
letta(JSON.stringify, {foo: 'bar'}, null, 2)
.then(data => {
console.log(data)
// =>
// {
// "foo": "bar"
// }
}, console.error)Synchronous functions
Again, showing correct handling of optinal arguments using native
fsmodule.
const fs = require('fs')
const letta = require('letta')
// sync function
letta(fs.statSync, 'package.json')
.then(stats => {
console.log(stats.isFile()) // => true
})
.catch(err => console.error(err.stack))
// correct handling of optional arguments
letta(fs.readFileSync, 'package.json')
.then(buf => {
console.log(Buffer.isBuffer(buf)) // => true
})
.catch(err => {
console.error(err.stack)
})Exceptions and rejections
Handles
uncaughtExceptionandunhandledRejectionby default.
Example
const fs = require('fs')
const letta = require('letta')
letta(fs.readFile, 'foobar.json')
.then(console.log, err => {
console.error(err.code) // => 'ENOENT'
})
// handles ReferenceError,
// SyntaxError and etc
const promise = letta(function () {
foo
return true
})
promise.catch(err => {
console.error(err) // => 'ReferenceError: foo is not defined'
})Returning errors
You should notice that if some function returns instance of
Errorit will acts as usual - receive it in.thennot in.catch. Review theexamples/errors.jsexample.
Example
const letta = require('letta')
const promise = letta(function () {
return new Error('foo err bar')
})
promise.then(errorAsResultValue => {
console.log(errorAsResultValue instanceof Error) // => true
console.log(errorAsResultValue.message) // => 'foo err bar'
})Passing function as last argument
You can also pass normal (non-callback) function as last argument without problem. It won't be assumed as callback, until you name it or have argument with some of common-callback-names.
Example
const assert = require('assert')
const letta = require('letta')
function regular (str, num, obj, fn) {
assert.strictEqual(str, 'foo')
assert.strictEqual(num, 123)
assert.deepEqual(obj, { a: 'b' })
assert.strictEqual(typeof str, 'string')
assert.strictEqual(typeof num, 'number')
assert.strictEqual(typeof obj, 'object')
assert.strictEqual(typeof fn, 'function')
return obj
}
letta(regular, 'foo', 123, {a: 'b'}, function someFn () {})
.then(result => {
console.log(result) // => { a: 'b' }
})Related
- callback2stream: Transform sync, async or generator function to Stream. Correctly handle errors. homepage
- letta-value: Extends
lettato accept and handles more than functions only. Handles all… more | homepage - mukla: Simple and fast test runner with basic reporter and clean stacktraces. Support… more | homepage
- promise2stream: Transform ES2015 Promise to Stream - specifically, Transform Stream using… more | homepage
- relike-all: Promisify all functions in an object, using relike. | homepage
- relike-value: Create promise from sync, async, string, number, array and so on. Handle… more | homepage
- relike: Simple promisify async or sync function with sane defaults. Lower level than… more | homepage
- value2stream: Transform any value to stream. Create a stream from any value -… more | homepage
Contributing
Pull requests and stars are always welcome. For bugs and feature requests, please create an issue. But before doing anything, please read the CONTRIBUTING.md guidelines.