3.0.1 • Published 6 years ago

promise-nodeify v3.0.1

Weekly downloads
15,748
License
MIT
Repository
github
Last release
6 years ago

promise-nodeify

Build Status: Linux Build Status: Windows Coverage Dependency Status Supported Node Version Version on NPM

Call a Node-style callback with the resolution value or rejection cause of a Promise without the common pitfalls.

Introductory Example

var promiseNodeify = require('promise-nodeify');

// Function which returns a Promise
function returnsPromise() {
  return new Promise(function(resolve, reject) {
    resolve(42);
  });
}

// Function which takes an optional node-style callback
function takesCallback(callback) {
  var promise = returnsPromise();
  // if callback is not a function, promise is returned as-is
  // otherwise callback will be called when promise is resolved or rejected
  // promise will not cause unhandledRejection if callback is a function
  return promiseNodeify(promise, callback);
}

Features

The important features of nodeify as compared to naive implementations:

  • Creates Error for falsey rejection causes. Since Promises may resolve or reject without providing a value or cause, the callback would have no way to distinguish success from failure. This module ensures the error argument is always truthy, substituting an Error when the rejection cause is falsey (and passing the original value as the .cause property, as bluebird does).
  • Exceptions thrown by callback cause uncaughtException as they would for other callbacks (unlike passing callback to .then, which causes unhandledRejection or swallows them).
  • The callback handles the promise rejection, preventing unhandledRejection (unlike if the promise were ignored and callback invoked directly).
  • Reduces confusion by only returning a Promise when no callback is given (as opposed to returning the promise argument, which creates uncertainty about unhandledRejections and multiple threads of control - or returning the result passing the callback to .then, which resolves to the callback result).

Behavior Comparison

This module provides similar behavior to several popular promise libraries in a promise-library-agnostic way which only requires the ES6 promise functionality subset. However, these existing implementations differ in subtle ways. A brief comparison:

Behaviorthis modulebluebird #asCallbackes-nodeifynodeifythen #nodeifyUn-thenify1when.js .bindCallback
returns (with function)undefinedthis Promise2undefinedPromise<undefined>undefinedundefinedwhen(promise)
returns (with falsey)promisepromisepromisePromise<undefined>promiseundefined with unhandledRejectionwhen(promise)
returns (non-function)promisepromiseundefined with unhandledRejectionpromisepromiseundefined with unhandledRejectionwhen(promise) with uncaughtException
callback exceptionuncaughtExceptionuncaughtExceptionunhandledRejectionuncaughtExceptionuncaughtExceptionunhandledRejectionuncaughtException
falsey causeError with .causeError with .cause3Errorfalsey causefalsey causeTypeErrorfalsey cause
reject argument length1111112
resolve argument length2undefined ? 1 : 2422222
extra argumentignoredoptions5ignoredignoredthis of callbackignoredignored

Notes:

  1. Un-thenify serves a similar purpose, but wraps the Promise-returning function rather than taking the Promise as an argument.
  2. Temporarily reverted in https://github.com/petkaantonov/bluebird/issues/151 and restored in https://github.com/petkaantonov/bluebird/issues/168
  3. In response to https://github.com/petkaantonov/bluebird/issues/434
  4. In response to https://github.com/petkaantonov/bluebird/issues/170
  5. Supports the spread boolean option to pass Array values as separate arguments to callback.

Performance Comparison

These benchmarks were done using the [benchmark/index.js](benchmark/index.js) script on an Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz with Node v4.3.1 on Linux and the following module versions:

ModuleVersion
[benchmark][npm-benchmark]2.1.0
[bluebird][npm-bluebird]3.3.3
[cli-table][npm-cli-table]0.3.1
[es-nodeify][npm-es-nodeify]1.0.0
[microtime][npm-microtime]2.0.0
[native-promise-only][npm-native-promise-only]0.8.1
[nodeify][npm-nodeify]1.0.0
[pinkie-promise][npm-pinkie-promise]2.0.0
[promise][npm-promise]7.1.1
[q][npm-q]1.4.1
[rsvp][npm-rsvp]3.2.1
[unthenify][npm-unthenify]1.0.1
[when][npm-when]3.7.7

Nodeify Resolved Promise

Performance (in operations per second) of calling nodeify on a resolved promise (larger is better):

ops/secbluebirdnativenpopinkieqrsvpthenwhen
bluebird#nodeify1,922,721.987TypeErrorTypeErrorTypeErrorTypeErrorTypeErrorTypeErrorTypeError
es-nodeify1,345,702.588506,103.345510,887.217534,013.96168,915.8161,974,250.7372,096,468.1191,756,177.934
nodeify147,481.019251,414.264251,535.145253,880.99858,504.0981,355,812.4821,102,467.7561,160,226.624
promiseNodeify1,586,092.279481,842.79452,529.247455,657.06266,045.2732,108,607.1262,370,823.7231,942,722.539
then#nodeify136,716.987202,670.23225,297.257231,042.28656,384.953764,719.551,320,158.92739,062.155
unthenify100,638.92279,097.9980,488.2578,298.36540,683.82103,125.162100,618.139101,887.997
when.bindCallback823.326856.669842.975834.864748.669847.556850.316839.995

Nodeify Rejected Promise

Performance (in operations per second) of calling nodeify on a rejected promise (larger is better):

ops/secbluebirdnativenpopinkieqrsvpthenwhen
bluebird#nodeify1,889,496.469TypeErrorTypeErrorTypeErrorTypeErrorTypeErrorTypeErrorTypeError
es-nodeify1,247,981.228520,349.959455,337.77466,964.69264,703.2472,182,281.0052,062,330.0351,889,184.935
nodeify147,454.87325,956.476326,958.556325,971.63753,878.0981,232,726.201952,338.091926,626.949
promiseNodeify1,170,756.604465,186.326478,343.59489,024.09462,905.8012,097,277.3711,928,682.9431,497,451.328
then#nodeify131,588.987241,627.02246,557.24245,427.55349,655.492684,232.8641,178,175.996634,041.464
unthenify96,359.91682,291.67982,507.05583,324.58438,842.74196,432.33297,113.0599,892.099
when.bindCallback822.083837.698848.358851.348789.546854.184844.102851.644

Installation

NPM

[This package](https://www.npmjs.com/package/promise-nodeify) can be installed using [npm](https://www.npmjs.com/) by running:

npm install promise-nodeify

Browser

This package can be installed using [bower](http://bower.io/) by running:

bower install promise-nodeify

Without Package Manager

This module is also available with a [UMD](https://github.com/umdjs/umd) loader, both minified and un-minified, in the [dist directory](dist). They can be downloaded, self-hosted, or loaded from a CDN. To use the [RawGit CDN](https://rawgit.com/), use the following (X)HTML:

<script src="https://cdn.rawgit.com/kevinoid/promise-nodeify/v0.1.0/dist/promise-nodeify.min.js"></script>

Recipes

Delegate to Promise.prototype.nodeify

If the behavior differences discussed in the [Behavior Comparison](#behavior-comparison) section (and any future differences which may occur) are not significant to your use case and you are interested in taking advantage of the potential [performance benefit](#performance-comparison) of the implementation provided by the promise library, use the .delegated function:

// Using .delegated delegates to .nodeify on the promise argument when present
var promiseNodeify = require('promise-nodeify').delegated;

function returnsPromise() {
  return new Promise(function(resolve, reject) {
    resolve(42);
  });
}

function takesCallback(callback) {
  var promise = returnsPromise();
  return promiseNodeify(promise, callback);
}

Polyfill Promise.prototype.nodeify

To polyfill the .nodeify (or .asCallback) method for a Promise library, assign the .nodeifyThis function to Promise.prototype.nodeify as follows:

Promise.prototype.nodeify = require('promise-nodeify').nodeifyThis;

function returnsPromise() {
  return new Promise(function(resolve, reject) {
    resolve(42);
  });
}

function takesCallback(callback) {
  var promise = returnsPromise();
  return promise.nodeify(callback);
}

More examples can be found in the [test specifications](https://kevinoid.github.io/promise-nodeify/spec).

API Docs

For a description of the available functions and their arguments, see the [API Documentation](https://kevinoid.github.io/promise-nodeify/api).

Contributing

Contributions are welcome and very much appreciated! Please add tests to cover any changes and ensure npm test passes.

The dist files are only updated for releases, so please don't include them in pull requests.

If the desired change is large, complex, backwards-incompatible, can have significantly differing implementations, or may not be in scope for this project, opening an issue before writing the code can avoid frustration and save a lot of time and effort.

License

This package is available under the terms of the [MIT License](https://opensource.org/licenses/MIT).