188 lines
5.5 KiB
Markdown
188 lines
5.5 KiB
Markdown
# deferential
|
|
|
|
es6 Native Promise Defer that helps build promise/callback dual APIS
|
|
|
|
[](http://travis-ci.org/eugeneware/deferential)
|
|
|
|
## Installation
|
|
|
|
This module is installed via npm:
|
|
|
|
``` bash
|
|
$ npm install deferential
|
|
```
|
|
|
|
## Background
|
|
|
|
It is very easy to produce APIs that are equally consumable with node callbacks
|
|
as *well* as promises.
|
|
|
|
Various promise libraries such as
|
|
[Q](https://github.com/kriskowal/q/wiki/API-Reference#deferredmakenoderesolver)
|
|
and [bluebird](http://bluebirdjs.com/docs/api/ascallback.html) have methods
|
|
to either convert Promises or Deferred objects into forms that make it easy to
|
|
adapt existing node.js APIS to support these *DUAL* APIs.
|
|
|
|
However, as of ES6 (and node 0.12), Promises are native in Javascript, and thus
|
|
the need to have a heavy kitchen-sink API like Q or Bluebird is no longer
|
|
necessary as we get fast native-only implementations of Promises.
|
|
|
|
And we can easily polyfill these with great libries such as
|
|
[native-promise-only](https://github.com/getify/native-promise-only).
|
|
|
|
Thus, we can create some small focused, modules to add these additional
|
|
features that should work with any native Promise implementation.
|
|
|
|
## Making a dual API function
|
|
|
|
Say you have a regular function tht returns the contents of a file.
|
|
|
|
Here is the callback version:
|
|
|
|
``` js
|
|
var fs = require('fs');
|
|
|
|
function getFile(fileName, cb) {
|
|
fs.readFile(fileName, 'utf8', cb);
|
|
}
|
|
|
|
getFile('myfile.text', function (err, data) {
|
|
if (err) return console.error(err);
|
|
console.log(data);
|
|
});
|
|
```
|
|
|
|
Here is the promise version:
|
|
|
|
``` js
|
|
var Promise = require('native-promise-only'),
|
|
fs = require('fs');
|
|
|
|
function getFile(fileName, cb) {
|
|
var p = new Promise(function (resolve, reject) {
|
|
fs.readFile(fileName, 'utf8', function (err, data) {
|
|
if (err) return reject(err);
|
|
resolve(data);
|
|
});
|
|
});
|
|
return p;
|
|
}
|
|
|
|
getFile('myfile.txt')
|
|
.then(function (data) {
|
|
console.log(data);
|
|
})
|
|
.catch(function (err) {
|
|
console.error(err);
|
|
});
|
|
```
|
|
|
|
Here is a version that supports both!
|
|
|
|
``` js
|
|
var Promise = require('native-promise-only'),
|
|
fs = require('fs'),
|
|
Deferred = require('deferential');
|
|
|
|
function getFile(fileName, cb) {
|
|
var d = Deferred();
|
|
fs.readFile(fileName, 'utf8', d.resolver());
|
|
return d.nodeify(cb);
|
|
}
|
|
|
|
// Use with callback
|
|
getFile('myfile.text', function (err, data) {
|
|
if (err) return console.error(err);
|
|
console.log(data);
|
|
});
|
|
|
|
// Use with promise
|
|
getFile('myfile.txt')
|
|
.then(function (data) {
|
|
console.log(data);
|
|
})
|
|
.catch(function (err) {
|
|
console.error(err);
|
|
});
|
|
```
|
|
|
|
The first line creats a new `Deferred` object:
|
|
|
|
``` js
|
|
var d = Deferred();
|
|
```
|
|
|
|
The `d.resolver()` returns a callback `thunk` which a standard node.js
|
|
callback function can call, and then depending on the error state, it will
|
|
`resolve()` or `reject()` the underlying promise (represented as `d.promise`).
|
|
|
|
The last line detects whether a `cb` callback arguments was passed in, and if
|
|
it is, it will callback the supplied `cb` based on the success or failure of the
|
|
underlying promise:
|
|
|
|
``` js
|
|
return d.nodeify(cb);
|
|
```
|
|
|
|
If the `cb` argument is missing (ie. `undefined`) then `d.nodeify()` returns
|
|
the underlying promise so that the function can be used as a regular promise
|
|
and chained with `.then()` and `.catch()` calls.
|
|
|
|
So, in summary, if a `cb` parameter is passed in `d.nodeify()` will call the
|
|
callback as normal and all is good to use the function as a regular callback.
|
|
|
|
If the `cb` parameter is missing, then a promise is returned.
|
|
|
|
The `Deferred` object has `resolve()` and `reject()` methods on it to help
|
|
resolve/reject the state of the underlying `Promise`. But there is also a helper
|
|
method called `Deferred#resolver()` which returns a `thunk` that can easily
|
|
passed into the callback paramter of regular node.js functions to automate
|
|
the tedious `if (err) return d.reject(err)` logic.
|
|
|
|
## API
|
|
|
|
### `Deferred()`
|
|
|
|
Creates a new instance of a Deferred. It can be created with or without the
|
|
`new` operator.
|
|
|
|
### `Deferred#resolve(value)`
|
|
|
|
Resolve the underlying `Promise`.
|
|
|
|
### `Deferred#reject(err)`
|
|
|
|
Reject the underlying `Promise` with an error.
|
|
|
|
### `Deferred#promise`
|
|
|
|
Return the underlying `Promise`. NB: This is a Native Promise as the underlying
|
|
library uses [native-promise-only](https://github.com/getify/native-promise-only)
|
|
which will use the underlying Native `Promise` implementation or a native
|
|
polyfill without all the guff.
|
|
|
|
### `Deferred#resolver()`
|
|
|
|
Returns a node.js `thunk` (a function with the signature `cb(err, results)`.
|
|
|
|
Pass this to a node.js style callback and then based on the result of the
|
|
callback, the undelrying `Promise` will be resolved/rejected.
|
|
|
|
### `Deferred#nodeify(cb, [opts])`
|
|
|
|
Call the provided `cb` node.js callback function if the underlying promise
|
|
is resolved/rejected. If not, return the underlying promise to allow for
|
|
regular `Promise` thenable chaining.
|
|
|
|
* `cb` - node callback that will be called when the underlying `Deferred#promise`
|
|
is resolved/rejected.
|
|
* `opts`:
|
|
* `spread` - (default: `false`). When `true` when multiple return arguments
|
|
are provided by the `#resolver()`, they will be mapped to additional
|
|
return arguments in the callback. This is because `Promises` can only
|
|
return a single value, whereas node.js callbacks can return multiple
|
|
return values (eg. `cb(null, val1, val2)`. If this is `false` and
|
|
multiple return values are returned, then the multiple values will
|
|
be returned as a single array of the return values. See the tests for
|
|
more details.
|