Browse Source

Merge pull request #25 from defunctzombie/promisify-callbackify

Add util.promisify and util.callbackify
inspect
Renée Kooi 6 years ago
committed by GitHub
parent
commit
2c65d292ba
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      package.json
  2. 169
      test/browser/callbackify.js
  3. 2
      test/browser/index.js
  4. 205
      test/browser/promisify.js
  5. 120
      test/node/callbackify-async.js
  6. 190
      test/node/callbackify.js
  7. 31
      test/node/common.js
  8. 4
      test/node/index.js
  9. 201
      test/node/promisify.js
  10. 117
      util.js

1
package.json

@ -29,6 +29,7 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"airtap": "0.0.6", "airtap": "0.0.6",
"is-async-supported": "~1.2.0",
"run-series": "~1.1.4", "run-series": "~1.1.4",
"tape": "~4.9.0" "tape": "~4.9.0"
}, },

169
test/browser/callbackify.js

@ -0,0 +1,169 @@
'use strict';
var test = require('tape');
var callbackify = require('../../').callbackify;
if (typeof Promise === 'undefined') {
console.log('no global Promise found, skipping callbackify tests');
return;
}
function after (n, cb) {
var i = 0;
return function () {
if (++i === n) cb();
}
}
var values = [
'hello world',
null,
undefined,
false,
0,
{},
{ key: 'value' },
function ok() {},
['array', 'with', 4, 'values'],
new Error('boo')
];
if (typeof Symbol !== 'undefined') {
values.push(Symbol('I am a symbol'));
}
test('util.callbackify resolution value is passed as second argument to callback', function (t) {
var end = after(values.length * 2, t.end);
// Test that the resolution value is passed as second argument to callback
values.forEach(function(value) {
// Test Promise factory
function promiseFn() {
return Promise.resolve(value);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(function(err, ret) {
t.ifError(err);
t.strictEqual(ret, value, 'cb ' + typeof value);
end();
});
// Test Thenable
function thenableFn() {
return {
then: function(onRes, onRej) {
onRes(value);
}
};
}
var cbThenableFn = callbackify(thenableFn);
cbThenableFn(function(err, ret) {
t.ifError(err);
t.strictEqual(ret, value, 'thenable ' + typeof value);
end();
});
});
});
test('util.callbackify rejection reason is passed as first argument to callback', function (t) {
var end = after(values.length * 2, t.end);
// Test that rejection reason is passed as first argument to callback
values.forEach(function(value) {
// test a Promise factory
function promiseFn() {
return Promise.reject(value);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(function(err, ret) {
t.strictEqual(ret, undefined, 'cb ' + typeof value);
if (err instanceof Error) {
if ('reason' in err) {
t.ok(!value);
t.strictEqual(err.message, 'Promise was rejected with a falsy value');
t.strictEqual(err.reason, value);
} else {
t.strictEqual(String(value).slice(-err.message.length), err.message);
}
} else {
t.strictEqual(err, value);
}
end();
});
// Test Thenable
function thenableFn() {
return {
then: function (onRes, onRej) {
onRej(value);
}
};
}
var cbThenableFn = callbackify(thenableFn);
cbThenableFn(function(err, ret) {
t.strictEqual(ret, undefined, 'thenable ' + typeof value);
if (err instanceof Error) {
if ('reason' in err) {
t.ok(!value);
t.strictEqual(err.message, 'Promise was rejected with a falsy value');
t.strictEqual(err.reason, value);
} else {
t.strictEqual(String(value).slice(-err.message.length), err.message);
}
} else {
t.strictEqual(err, value);
}
end();
});
});
});
test('util.callbackify arguments passed to callbackified function are passed to original', function (t) {
var end = after(values.length, t.end);
// Test that arguments passed to callbackified function are passed to original
values.forEach(function(value) {
function promiseFn(arg) {
t.strictEqual(arg, value);
return Promise.resolve(arg);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(value, function(err, ret) {
t.ifError(err);
t.strictEqual(ret, value);
end();
});
});
});
test('util.callbackify `this` binding is the same for callbackified and original', function (t) {
var end = after(values.length, t.end);
// Test that `this` binding is the same for callbackified and original
values.forEach(function(value) {
var iAmThis = {
fn: function(arg) {
t.strictEqual(this, iAmThis);
return Promise.resolve(arg);
},
};
iAmThis.cbFn = callbackify(iAmThis.fn);
iAmThis.cbFn(value, function(err, ret) {
t.ifError(err);
t.strictEqual(ret, value);
t.strictEqual(this, iAmThis);
end();
});
});
});
test('util.callbackify non-function inputs throw', function (t) {
// Verify that non-function inputs throw.
['foo', null, undefined, false, 0, {}, typeof Symbol !== 'undefined' ? Symbol() : undefined, []].forEach(function(value) {
t.throws(
function() { callbackify(value); },
'The "original" argument must be of type Function'
);
});
t.end();
});

2
test/browser/index.js

@ -1,2 +1,4 @@
require('./inspect'); require('./inspect');
require('./is'); require('./is');
require('./promisify');
require('./callbackify');

205
test/browser/promisify.js

@ -0,0 +1,205 @@
var promisify = require('../../').promisify;
var test = require('tape');
var hasSymbol = typeof Symbol !== 'undefined';
if (typeof Promise === 'undefined') {
console.log('no global Promise found, skipping promisify tests');
return;
}
var callbacker = function (arg, cb) {
setTimeout(function () {
if (typeof arg === 'string')
cb(null, arg.toUpperCase());
else cb(new TypeError('incorrect type'));
}, 5);
}
var promiser = promisify(callbacker);
test('util.promisify resolves', function (t) {
var promise = promiser(__filename);
t.ok(promise instanceof Promise);
promise.then(function (value) {
t.deepEqual(value, __filename.toUpperCase());
t.end();
});
});
test('util.promisify rejects', function (t) {
var promise = promiser(42);
promise.catch(function (error) {
t.equal(error.message, 'incorrect type');
t.end();
});
});
test('util.promisify custom', { skip: !hasSymbol }, function (t) {
function fn() {}
function promisifedFn() {}
fn[promisify.custom] = promisifedFn;
t.strictEqual(promisify(fn), promisifedFn);
t.strictEqual(promisify(promisify(fn)), promisifedFn);
t.end();
});
test('util.promisify custom of invalid type', { skip: !hasSymbol }, function (t) {
function fn2() {}
fn2[promisify.custom] = 42;
t.throws(
function () { promisify(fn2); },
/must be of type Function/
);
t.end();
});
test('util.promisify multiple callback values', function (t) {
function fn5(callback) {
callback(null, 'foo', 'bar');
}
promisify(fn5)().then(function (value) {
t.strictEqual(value, 'foo');
t.end();
});
});
test('util.promisify no callback success value', function (t) {
function fn6(callback) {
callback(null);
}
promisify(fn6)().then(function (value) {
t.strictEqual(value, undefined);
t.end();
});
});
test('util.promisify no callback arguments at all', function (t) {
function fn7(callback) {
callback();
}
promisify(fn7)().then(function (value) {
t.strictEqual(value, undefined);
t.end();
});
});
test('util.promisify passing arguments', function (t) {
function fn8(err, val, callback) {
callback(err, val);
}
promisify(fn8)(null, 42).then(function (value) {
t.strictEqual(value, 42);
t.end();
});
});
test('util.promisify passing arguments (rejects)', function (t) {
function fn9(err, val, callback) {
callback(err, val);
}
promisify(fn9)(new Error('oops'), null).catch(function (err) {
t.strictEqual(err.message, 'oops');
t.end();
});
});
test('util.promisify chain', function (t) {
function fn9(err, val, callback) {
callback(err, val);
}
Promise.resolve()
.then(function () { return promisify(fn9)(null, 42); })
.then(function (value) {
t.strictEqual(value, 42);
t.end();
});
});
test('util.promisify keeps correct `this`', function (t) {
var o = {};
var fn10 = promisify(function(cb) {
cb(null, this === o);
});
o.fn = fn10;
o.fn().then(function(val) {
t.ok(val);
t.end();
});
});
test('util.promisify calling callback multiple times', function (t) {
var err = new Error('Should not have called the callback with the error.');
var stack = err.stack;
var fn11 = promisify(function(cb) {
cb(null);
cb(err);
});
Promise.resolve()
.then(function () { return fn11(); })
.then(function () { return Promise.resolve(); })
.then(function () {
t.strictEqual(stack, err.stack);
t.end();
});
});
// Can't do this without Symbol() unfortunately
test('util.promisify promisifying a promisified function', { skip: !hasSymbol }, function (t) {
function c() { }
var a = promisify(function() { });
var b = promisify(a);
t.notStrictEqual(c, a);
t.strictEqual(a, b);
t.end();
});
test('util.promisify sync throw becomes rejection', function (t) {
var errToThrow;
var thrower = promisify(function(a, b, c, cb) {
errToThrow = new Error();
throw errToThrow;
});
thrower(1, 2, 3)
.then(t.fail)
.then(t.fail, function (e) {
t.strictEqual(e, errToThrow);
t.end();
});
});
test('util.promisify callback and sync throw', function (t) {
var err = new Error();
var a = promisify(function (cb) { cb(err) })();
var b = promisify(function () { throw err; })();
Promise.all([
a.then(t.fail, function(e) {
t.strictEqual(err, e);
}),
b.then(t.fail, function(e) {
t.strictEqual(err, e);
})
]).then(function () {
t.end();
});
});
test('util.promisify throws for incorrect argument types', function (t) {
var array = [undefined, null, true, 0, 'str', {}, []];
if (typeof Symbol !== 'undefined') array.push(Symbol());
array.forEach(function (input) {
t.throws(
function () { promisify(input); },
'The "original" argument must be of type Function'
);
});
t.end();
});

120
test/node/callbackify-async.js

@ -0,0 +1,120 @@
'use strict';
// Separate test file for tests using new syntax (async/await).
var common = require('./common');
var assert = require('assert');
var callbackify = require('../../').callbackify;
var execFile = require('child_process').execFile;
var values = [
'hello world',
null,
undefined,
false,
0,
{},
{ key: 'value' },
Symbol('I am a symbol'),
function ok() {},
['array', 'with', 4, 'values'],
new Error('boo')
];
{
// Test that the resolution value is passed as second argument to callback
values.forEach(function(value) {
// Test and `async function`
async function asyncFn() {
return value;
}
var cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
});
}
{
// Test that rejection reason is passed as first argument to callback
values.forEach(function(value) {
// Test an `async function`
async function asyncFn() {
return Promise.reject(value);
}
var cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(common.mustCall(function (err, ret) {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.message, 'Promise was rejected with a falsy value');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
});
}
{
// Test that arguments passed to callbackified function are passed to original
values.forEach(function(value) {
async function asyncFn(arg) {
assert.strictEqual(arg, value);
return arg;
}
var cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
});
}
{
// Test that `this` binding is the same for callbackified and original
values.forEach(function(value) {
var iAmThat = {
async fn(arg) {
assert.strictEqual(this, iAmThat);
return arg;
},
};
iAmThat.cbFn = callbackify(iAmThat.fn);
iAmThat.cbFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
assert.strictEqual(this, iAmThat);
}));
});
}
{
async function asyncFn() {
return 42;
}
var cb = callbackify(asyncFn);
var args = [];
// Verify that the last argument to the callbackified function is a function.
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach(function(value) {
args.push(value);
common.expectsError(function() {
cb(...args);
}, {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The last argument must be of type Function'
});
});
}

190
test/node/callbackify.js

@ -0,0 +1,190 @@
'use strict';
// This test checks that the semantics of `util.callbackify` are as described in
// the API docs
var common = require('./common');
var assert = require('assert');
var callbackify = require('../../').callbackify;
var execFile = require('child_process').execFile;
var values = [
'hello world',
null,
undefined,
false,
0,
{},
{ key: 'value' },
function ok() {},
['array', 'with', 4, 'values'],
new Error('boo')
];
if (typeof Symbol !== 'undefined') {
values.push(Symbol('I am a symbol'));
}
{
// Test that the resolution value is passed as second argument to callback
values.forEach(function(value) {
// Test Promise factory
function promiseFn() {
return Promise.resolve(value);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
// Test Thenable
function thenableFn() {
return {
then: function(onRes, onRej) {
onRes(value);
}
};
}
var cbThenableFn = callbackify(thenableFn);
cbThenableFn(common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
});
}
{
// Test that rejection reason is passed as first argument to callback
values.forEach(function(value) {
// test a Promise factory
function promiseFn() {
return Promise.reject(value);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(common.mustCall(function(err, ret) {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.message, 'Promise was rejected with a falsy value');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
// Test Thenable
function thenableFn() {
return {
then: function (onRes, onRej) {
onRej(value);
}
};
}
var cbThenableFn = callbackify(thenableFn);
cbThenableFn(common.mustCall(function(err, ret) {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.message, 'Promise was rejected with a falsy value');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
});
}
{
// Test that arguments passed to callbackified function are passed to original
values.forEach(function(value) {
function promiseFn(arg) {
assert.strictEqual(arg, value);
return Promise.resolve(arg);
}
var cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
});
}
{
// Test that `this` binding is the same for callbackified and original
values.forEach(function(value) {
var iAmThis = {
fn: function(arg) {
assert.strictEqual(this, iAmThis);
return Promise.resolve(arg);
},
};
iAmThis.cbFn = callbackify(iAmThis.fn);
iAmThis.cbFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
assert.strictEqual(this, iAmThis);
}));
});
}
// These tests are not necessary in the browser.
if (false) {
// Test that callback that throws emits an `uncaughtException` event
var fixture = fixtures.path('uncaught-exceptions', 'callbackify1.js');
execFile(
process.execPath,
[fixture],
common.mustCall(function (err, stdout, stderr) {
assert.strictEqual(err.code, 1);
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error');
assert.strictEqual(stdout, '');
var errLines = stderr.trim().split(/[\r\n]+/);
var errLine = errLines.find(function (l) { return /^Error/.exec(l) });
assert.strictEqual(errLine, 'Error: ' + fixture);
})
);
}
if (false) {
// Test that handled `uncaughtException` works and passes rejection reason
var fixture = fixtures.path('uncaught-exceptions', 'callbackify2.js');
execFile(
process.execPath,
[fixture],
common.mustCall(function (err, stdout, stderr) {
assert.ifError(err);
assert.strictEqual(stdout.trim(), fixture);
assert.strictEqual(stderr, '');
})
);
}
{
// Verify that non-function inputs throw.
['foo', null, undefined, false, 0, {}, typeof Symbol !== 'undefined' ? Symbol() : undefined, []].forEach(function(value) {
common.expectsError(function() {
callbackify(value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "original" argument must be of type Function'
});
});
}
if (require('is-async-supported')()) {
require('./callbackify-async');
}

31
test/node/common.js

@ -0,0 +1,31 @@
var assert = require('assert');
var mustCalls = [];
var common = {
expectsError: function (fn, props) {
try { fn(); }
catch (err) {
if (props.type) assert.equal(err.constructor, props.type);
if (props.message) assert.equal(err.message, props.message);
return;
}
assert.fail('expected error');
},
mustCall: function (fn) {
function mustCall() {
mustCall.called = true
return fn.apply(this, arguments);
}
mustCalls.push(mustCall);
return mustCall;
}
};
process.on('exit', function () {
mustCalls.forEach(function (mc) {
assert(mc.called);
});
});
module.exports = common;

4
test/node/index.js

@ -15,5 +15,7 @@ series([
test(require.resolve('./debug')), test(require.resolve('./debug')),
test(require.resolve('./format')), test(require.resolve('./format')),
test(require.resolve('./inspect')), test(require.resolve('./inspect')),
test(require.resolve('./log')) test(require.resolve('./log')),
test(require.resolve('./promisify')),
test(require.resolve('./callbackify'))
]); ]);

201
test/node/promisify.js

@ -0,0 +1,201 @@
var common = require('./common');
var assert = require('assert');
var fs = require('fs');
var vm = require('vm');
var promisify = require('../../util').promisify;
if (typeof Promise === 'undefined') {
console.log('no global Promise found, skipping promisify tests');
return;
}
var stat = promisify(fs.stat);
{
var promise = stat(__filename);
assert(promise instanceof Promise);
promise.then(common.mustCall(function (value) {
assert.deepStrictEqual(value, fs.statSync(__filename));
}));
}
{
var promise = stat('/dontexist');
promise.catch(common.mustCall(function (error) {
assert(error.message.indexOf('ENOENT: no such file or directory, stat') !== -1);
}));
}
{
function fn() {}
function promisifedFn() {}
fn[promisify.custom] = promisifedFn;
assert.strictEqual(promisify(fn), promisifedFn);
assert.strictEqual(promisify(promisify(fn)), promisifedFn);
}
{
function fn2() {}
fn2[promisify.custom] = 42;
common.expectsError(
function () { promisify(fn2); },
{ code: 'ERR_INVALID_ARG_TYPE', type: TypeError }
);
}
// promisify args test disabled, it is an internal core API that is
// not used anywhere anymore and this package does not implement it.
if (false) {
var firstValue = 5;
var secondValue = 17;
function fn3(callback) {
callback(null, firstValue, secondValue);
}
fn3[customPromisifyArgs] = ['first', 'second'];
promisify(fn3)().then(common.mustCall(function (obj) {
assert.deepStrictEqual(obj, { first: firstValue, second: secondValue });
}));
}
{
var fn4 = vm.runInNewContext('(function() {})');
assert.notStrictEqual(Object.getPrototypeOf(promisify(fn4)),
Function.prototype);
}
{
function fn5(callback) {
callback(null, 'foo', 'bar');
}
promisify(fn5)().then(common.mustCall(function (value) {
assert.deepStrictEqual(value, 'foo');
}));
}
{
function fn6(callback) {
callback(null);
}
promisify(fn6)().then(common.mustCall(function (value) {
assert.strictEqual(value, undefined);
}));
}
{
function fn7(callback) {
callback();
}
promisify(fn7)().then(common.mustCall(function (value) {
assert.strictEqual(value, undefined);
}));
}
{
function fn8(err, val, callback) {
callback(err, val);
}
promisify(fn8)(null, 42).then(common.mustCall(function (value) {
assert.strictEqual(value, 42);
}));
}
{
function fn9(err, val, callback) {
callback(err, val);
}
promisify(fn9)(new Error('oops'), null).catch(common.mustCall(function (err) {
assert.strictEqual(err.message, 'oops');
}));
}
{
function fn9(err, val, callback) {
callback(err, val);
}
Promise.resolve()
.then(function () { return promisify(fn9)(null, 42); })
.then(function (value) {
assert.strictEqual(value, 42);
});
}
{
var o = {};
var fn10 = promisify(function(cb) {
cb(null, this === o);
});
o.fn = fn10;
o.fn().then(common.mustCall(function(val) {
assert(val);
}));
}
(function () {
var err = new Error('Should not have called the callback with the error.');
var stack = err.stack;
var fn11 = promisify(function(cb) {
cb(null);
cb(err);
});
Promise.resolve()
.then(function () { return fn11(); })
.then(function () { return Promise.resolve(); })
.then(function () {
assert.strictEqual(stack, err.stack);
});
})();
{
function c() { }
var a = promisify(function() { });
var b = promisify(a);
assert.notStrictEqual(c, a);
assert.strictEqual(a, b);
}
{
var errToThrow;
var thrower = promisify(function(a, b, c, cb) {
errToThrow = new Error();
throw errToThrow;
});
thrower(1, 2, 3)
.then(assert.fail)
.then(assert.fail, function (e) { assert.strictEqual(e, errToThrow); });
}
{
var err = new Error();
var a = promisify(function (cb) { cb(err) })();
var b = promisify(function () { throw err; })();
Promise.all([
a.then(assert.fail, function(e) {
assert.strictEqual(err, e);
}),
b.then(assert.fail, function(e) {
assert.strictEqual(err, e);
})
]);
}
[undefined, null, true, 0, 'str', {}, [], Symbol()].forEach(function (input) {
common.expectsError(
function () { promisify(input); },
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "original" argument must be of type Function'
});
});

117
util.js

@ -19,6 +19,16 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
function getOwnPropertyDescriptors(obj) {
var keys = Object.keys(obj);
var descriptors = {};
for (var i = 0; i < keys.length; i++) {
descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);
}
return descriptors;
};
var formatRegExp = /%[sdj%]/g; var formatRegExp = /%[sdj%]/g;
exports.format = function(f) { exports.format = function(f) {
if (!isString(f)) { if (!isString(f)) {
@ -584,3 +594,110 @@ exports._extend = function(origin, add) {
function hasOwnProperty(obj, prop) { function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop); return Object.prototype.hasOwnProperty.call(obj, prop);
} }
var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined;
exports.promisify = function promisify(original) {
if (typeof original !== 'function')
throw new TypeError('The "original" argument must be of type Function');
if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) {
var fn = original[kCustomPromisifiedSymbol];
if (typeof fn !== 'function') {
throw new TypeError('The "util.promisify.custom" argument must be of type Function');
}
Object.defineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
return fn;
}
function fn() {
var promiseResolve, promiseReject;
var promise = new Promise(function (resolve, reject) {
promiseResolve = resolve;
promiseReject = reject;
});
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
args.push(function (err, value) {
if (err) {
promiseReject(err);
} else {
promiseResolve(value);
}
});
try {
original.apply(this, args);
} catch (err) {
promiseReject(err);
}
return promise;
}
Object.setPrototypeOf(fn, Object.getPrototypeOf(original));
if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, {
value: fn, enumerable: false, writable: false, configurable: true
});
return Object.defineProperties(
fn,
getOwnPropertyDescriptors(original)
);
}
exports.promisify.custom = kCustomPromisifiedSymbol
function callbackifyOnRejected(reason, cb) {
// `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
// Because `null` is a special error value in callbacks which means "no error
// occurred", we error-wrap so the callback consumer can distinguish between
// "the promise rejected with null" or "the promise fulfilled with undefined".
if (!reason) {
var newReason = new Error('Promise was rejected with a falsy value');
newReason.reason = reason;
reason = newReason;
}
return cb(reason);
}
function callbackify(original) {
if (typeof original !== 'function') {
throw new TypeError('The "original" argument must be of type Function');
}
// We DO NOT return the promise as it gives the user a false sense that
// the promise is actually somehow related to the callback's execution
// and that the callback throwing will reject the promise.
function callbackified() {
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
var maybeCb = args.pop();
if (typeof maybeCb !== 'function') {
throw new TypeError('The last argument must be of type Function');
}
var self = this;
var cb = function() {
return maybeCb.apply(self, arguments);
};
// In true node style we process the callback on `nextTick` with all the
// implications (stack, `uncaughtException`, `async_hooks`)
original.apply(this, args)
.then(function(ret) { process.nextTick(cb, null, ret) },
function(rej) { process.nextTick(callbackifyOnRejected, rej, cb) });
}
Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
Object.defineProperties(callbackified,
getOwnPropertyDescriptors(original));
return callbackified;
}
exports.callbackify = callbackify;

Loading…
Cancel
Save