Skip to content

Commit

Permalink
Merge pull request #65 from MichaelDeBoey/add-callbackify-promisify-t…
Browse files Browse the repository at this point in the history
…o-util

feat(util): add `callbackify` & `promisify`
  • Loading branch information
FredKSchott authored Jan 24, 2023
2 parents f76fd4c + cb544ce commit fb2d27c
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 3 deletions.
2 changes: 1 addition & 1 deletion polyfills/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export var UV_FS_COPYFILE_FICLONE = 2;
export var COPYFILE_FICLONE = 2;
export var UV_FS_COPYFILE_FICLONE_FORCE = 4;
export var COPYFILE_FICLONE_FORCE = 4;
export var OPENSSL_VERSION_NUMBER = 269488351;
export var OPENSSL_VERSION_NUMBER = 269488335;
export var SSL_OP_ALL = 2147485780;
export var SSL_OP_ALLOW_NO_DHE_KEX = 1024;
export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 262144;
Expand Down
120 changes: 119 additions & 1 deletion polyfills/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import process from 'process';

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;
export function format(f) {
if (!isString(f)) {
Expand Down Expand Up @@ -572,6 +583,111 @@ function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}

var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined;

export 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)
);
}

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);
}

export 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.bind(null, null, ret)) },
function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) });
}

Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
Object.defineProperties(callbackified, getOwnPropertyDescriptors(original));
return callbackified;
}

export default {
inherits: inherits,
_extend: _extend,
Expand All @@ -594,5 +710,7 @@ export default {
inspect: inspect,
deprecate: deprecate,
format: format,
debuglog: debuglog
debuglog: debuglog,
promisify: promisify,
callbackify: callbackify,
}
2 changes: 1 addition & 1 deletion src/polyfills.ts

Large diffs are not rendered by default.

0 comments on commit fb2d27c

Please sign in to comment.