Skip to content

Commit

Permalink
feat: support options.multiArgs and options.withCallback (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
dead-horse authored May 19, 2017
1 parent a9531d6 commit 9a74a1c
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 12 deletions.
61 changes: 58 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,28 @@ Promisify a callback-based function using [`any-promise`](https://github.com/kev

- Preserves function names
- Uses a native promise implementation if available and tries to fall back to a promise implementation such as `bluebird`
- Converts multiple arguments from the callback into an `Array`
- Converts multiple arguments from the callback into an `Array`, also support change the behavior by `options.multiArgs`
- Resulting function never deoptimizes
- Supports both callback and promise style

An added benefit is that `throw`n errors in that async function will be caught by the promise!

## API

### fn = thenify(fn, options)

Promisifies a function.

### Options

`options` are optional.

- `options.withCallback` - support both callback and promise style, default to `false`.
- `options.multiArgs` - change the behavior when callback have multiple arguments. default to `true`.
- `true` - converts multiple arguments to an array
- `false`- always use the first argument
- `Array` - converts multiple arguments to an object with keys provided in `options.multiArgs`

- Turn async functions into promises

```js
Expand All @@ -32,6 +46,19 @@ var somethingAsync = thenify(function somethingAsync(a, b, c, callback) {

- Backward compatible with callback

```js
var thenify = require('thenify');

var somethingAsync = thenify(function somethingAsync(a, b, c, callback) {
callback(null, a, b, c);
}, { withCallback: true });

// somethingAsync(a, b, c).then(onFulfilled).catch(onRejected);
// somethingAsync(a, b, c, function () {});
```

or use `thenify.withCallback()`

```js
var thenify = require('thenify').withCallback;

Expand All @@ -43,9 +70,37 @@ var somethingAsync = thenify(function somethingAsync(a, b, c, callback) {
// somethingAsync(a, b, c, function () {});
```

### var fn = thenify(fn)
- Always return the first argument in callback

Promisifies a function.
```js
var thenify = require('thenify');

var promise = thenify(function (callback) {
callback(null, 1, 2, 3);
}, { multiArgs: false });

// promise().then(function onFulfilled(value) {
// assert.equal(value, 1);
// });
```

- Converts callback arguments to an object

```js
var thenify = require('thenify');

var promise = thenify(function (callback) {
callback(null, 1, 2, 3);
}, { multiArgs: [ 'one', 'tow', 'three' ] });

// promise().then(function onFulfilled(value) {
// assert.deepEqual(value, {
// one: 1,
// tow: 2,
// three: 3
// });
// });
```

[gitter-image]: https://badges.gitter.im/thenables/thenify.png
[gitter-url]: https://gitter.im/thenables/thenify
Expand Down
35 changes: 26 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ module.exports = thenify
* @api public
*/

function thenify($$__fn__$$) {
function thenify($$__fn__$$, options) {
assert(typeof $$__fn__$$ === 'function')
return eval(createWrapper($$__fn__$$.name))
return eval(createWrapper($$__fn__$$.name, options))
}

/**
Expand All @@ -25,38 +25,55 @@ function thenify($$__fn__$$) {
* @api public
*/

thenify.withCallback = function ($$__fn__$$) {
thenify.withCallback = function ($$__fn__$$, options) {
assert(typeof $$__fn__$$ === 'function')
return eval(createWrapper($$__fn__$$.name, true))
options = options || {}
options.withCallback = true
if (options.multiArgs === undefined) options.multiArgs = true
return eval(createWrapper($$__fn__$$.name, options))
}

function createCallback(resolve, reject) {
function createCallback(resolve, reject, multiArgs) {
return function(err, value) {
if (err) return reject(err)
var length = arguments.length
if (length <= 2) return resolve(value)

if (length <= 2 || !multiArgs) return resolve(value)

if (Array.isArray(multiArgs)) {
var values = {}
for (var i = 1; i < length; i++) values[multiArgs[i - 1]] = arguments[i]
return resolve(values)
}

var values = new Array(length - 1)
for (var i = 1; i < length; ++i) values[i - 1] = arguments[i]
resolve(values)
}
}

function createWrapper(name, withCallback) {
function createWrapper(name, options) {
name = (name || '').replace(/\s|bound(?!$)/g, '')
withCallback = withCallback ?
options = options || {}
// default to true
var multiArgs = options.multiArgs !== undefined ? options.multiArgs : true
multiArgs = 'var multiArgs = ' + JSON.stringify(multiArgs) + '\n'

var withCallback = options.withCallback ?
'var lastType = typeof arguments[len - 1]\n'
+ 'if (lastType === "function") return $$__fn__$$.apply(self, arguments)\n'
: ''

return '(function ' + name + '() {\n'
+ 'var self = this\n'
+ 'var len = arguments.length\n'
+ multiArgs
+ withCallback
+ 'var args = new Array(len + 1)\n'
+ 'for (var i = 0; i < len; ++i) args[i] = arguments[i]\n'
+ 'var lastIndex = i\n'
+ 'return new Promise(function (resolve, reject) {\n'
+ 'args[lastIndex] = createCallback(resolve, reject)\n'
+ 'args[lastIndex] = createCallback(resolve, reject, multiArgs)\n'
+ '$$__fn__$$.apply(self, args)\n'
+ '})\n'
+ '})'
Expand Down
57 changes: 57 additions & 0 deletions test/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

var assert = require('assert')

var promisify = require('..')

var setImmediate = global.setImmediate || function (fn) {
process.nextTick(fn)
}

describe('options', function () {
describe('.withCallback', function () {
function fn(cb) {
cb(null, true)
}

it('promise', function () {
return promisify(fn, { withCallback: true })().then(function (val) {
assert.equal(val, true)
})
})

it('callback', function (done) {
return promisify(fn, { withCallback: true })(function (err, val) {
assert.equal(val, true)
done()
})
})
})

describe('.multiArgs', function () {
function fn(cb) {
cb(null, 1, 2, 3)
}

it('default to true', function () {
return promisify(fn)().then(function (values) {
assert.deepEqual(values, [1, 2, 3])
})
})

it('set to false', function () {
return promisify(fn, { multiArgs: false })().then(function (value) {
assert.equal(value, 1)
})
})

it('set to array', function () {
return promisify(fn, { multiArgs: [ 'one', 'tow', 'three' ] })().then(function (value) {
assert.deepEqual(value, {
one: 1,
tow: 2,
three: 3
})
})
})
})
})

0 comments on commit 9a74a1c

Please sign in to comment.