Call a Node-style callback with the resolution value or rejection cause of a Promise without the common pitfalls.
MIT License
Call a Node-style callback with the resolution value or rejection cause of a Promise without the common pitfalls.
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);
}
The important features of nodeify
as compared to naive implementations:
Error
for falsey rejection causes. Since Promises may resolve orError
when the rejection cause is falsey.cause
property, as bluebird does).uncaughtException
as they would for.then
, which causesunhandledRejection
or swallows them).unhandledRejection
unhandledRejection
s and multiple threads of control - or returning.then
, which resolves to the callbackThis 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:
Behavior | this module | bluebird #asCallback |
es-nodeify | nodeify | then #nodeify |
Un-thenify1 | when.js .bindCallback |
---|---|---|---|---|---|---|---|
returns (with function ) |
undefined |
this Promise 2
|
undefined |
Promise<undefined> |
undefined |
undefined |
when(promise) |
returns (with falsey) | promise |
promise |
promise |
Promise<undefined> |
promise |
undefined with unhandledRejection
|
when(promise) |
returns (non-function) | promise |
promise |
undefined with unhandledRejection
|
promise |
promise |
undefined with unhandledRejection
|
when(promise) with uncaughtException
|
callback exception | uncaughtException |
uncaughtException |
unhandledRejection |
uncaughtException |
uncaughtException |
unhandledRejection |
uncaughtException |
falsey cause |
Error with .cause
|
Error with .cause 3
|
Error |
falsey cause | falsey cause | TypeError |
falsey cause |
reject argument length | 1 | 1 | 1 | 1 | 1 | 1 | 2 |
resolve argument length | 2 |
undefined ? 1 : 24
|
2 | 2 | 2 | 2 | 2 |
extra argument | ignored | options5 | ignored | ignored |
this of callback |
ignored | ignored |
Notes:
spread
boolean option toArray
values as separate arguments to callback
.These benchmarks were done using the 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:
Module | Version |
---|---|
benchmark |
2.1.0 |
bluebird |
3.3.3 |
cli-table |
0.3.1 |
es-nodeify |
1.0.0 |
microtime |
2.0.0 |
native-promise-only |
0.8.1 |
nodeify |
1.0.0 |
pinkie-promise |
2.0.0 |
promise |
7.1.1 |
q |
1.4.1 |
rsvp |
3.2.1 |
unthenify |
1.0.1 |
when |
3.7.7 |
Performance (in operations per second) of calling nodeify
on a resolved
promise (larger is better):
ops/sec | bluebird | native | npo | pinkie | q | rsvp | then | when |
---|---|---|---|---|---|---|---|---|
bluebird#nodeify | 1,922,721.987 | TypeError | TypeError | TypeError | TypeError | TypeError | TypeError | TypeError |
es-nodeify | 1,345,702.588 | 506,103.345 | 510,887.217 | 534,013.961 | 68,915.816 | 1,974,250.737 | 2,096,468.119 | 1,756,177.934 |
nodeify | 147,481.019 | 251,414.264 | 251,535.145 | 253,880.998 | 58,504.098 | 1,355,812.482 | 1,102,467.756 | 1,160,226.624 |
promiseNodeify | 1,586,092.279 | 481,842.79 | 452,529.247 | 455,657.062 | 66,045.273 | 2,108,607.126 | 2,370,823.723 | 1,942,722.539 |
then#nodeify | 136,716.987 | 202,670.23 | 225,297.257 | 231,042.286 | 56,384.953 | 764,719.55 | 1,320,158.92 | 739,062.155 |
unthenify | 100,638.922 | 79,097.99 | 80,488.25 | 78,298.365 | 40,683.82 | 103,125.162 | 100,618.139 | 101,887.997 |
when.bindCallback | 823.326 | 856.669 | 842.975 | 834.864 | 748.669 | 847.556 | 850.316 | 839.995 |
Performance (in operations per second) of calling nodeify
on a rejected
promise (larger is better):
ops/sec | bluebird | native | npo | pinkie | q | rsvp | then | when |
---|---|---|---|---|---|---|---|---|
bluebird#nodeify | 1,889,496.469 | TypeError | TypeError | TypeError | TypeError | TypeError | TypeError | TypeError |
es-nodeify | 1,247,981.228 | 520,349.959 | 455,337.77 | 466,964.692 | 64,703.247 | 2,182,281.005 | 2,062,330.035 | 1,889,184.935 |
nodeify | 147,454.87 | 325,956.476 | 326,958.556 | 325,971.637 | 53,878.098 | 1,232,726.201 | 952,338.091 | 926,626.949 |
promiseNodeify | 1,170,756.604 | 465,186.326 | 478,343.59 | 489,024.094 | 62,905.801 | 2,097,277.371 | 1,928,682.943 | 1,497,451.328 |
then#nodeify | 131,588.987 | 241,627.02 | 246,557.24 | 245,427.553 | 49,655.492 | 684,232.864 | 1,178,175.996 | 634,041.464 |
unthenify | 96,359.916 | 82,291.679 | 82,507.055 | 83,324.584 | 38,842.741 | 96,432.332 | 97,113.05 | 99,892.099 |
when.bindCallback | 822.083 | 837.698 | 848.358 | 851.348 | 789.546 | 854.184 | 844.102 | 851.644 |
This package can be installed using npm by running:
npm install promise-nodeify
This package can be installed using bower by running:
bower install promise-nodeify
This module is also available with a UMD
loader, both minified and un-minified, in the dist
directory. They
can be downloaded, self-hosted, or loaded from a CDN. To use the RawGit
CDN, use the following (X)HTML:
<script src="https://cdn.rawgit.com/kevinoid/promise-nodeify/v0.1.0/dist/promise-nodeify.min.js"></script>
Promise.prototype.nodeify
If the behavior differences discussed in the 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 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);
}
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.
For a description of the available functions and their arguments, see the API Documentation.
Contributions are appreciated. Contributors agree to abide by the Contributor Covenant Code of Conduct. If this is your first time contributing to a Free and Open Source Software project, consider reading How to Contribute to Open Source in the Open Source Guides.
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.
This project is available under the terms of the MIT License. See the summary at TLDRLegal.