Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(util): add callbackify & promisify #65

Merged
merged 1 commit into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.