summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRefael Ackermann <refack@gmail.com>2017-04-27 21:57:12 -0400
committerRefael Ackermann <refack@gmail.com>2017-06-10 22:49:28 -0400
commitaf3aa682ac534bb55765f5fef2755a88e5ff2580 (patch)
treed632fb6f8347f58c05731d16374b05706ef828ba /lib
parent780acc2208a3cdd3b01ae93aeaa478771fd3fd56 (diff)
downloadandroid-node-v8-af3aa682ac534bb55765f5fef2755a88e5ff2580.tar.gz
android-node-v8-af3aa682ac534bb55765f5fef2755a88e5ff2580.tar.bz2
android-node-v8-af3aa682ac534bb55765f5fef2755a88e5ff2580.zip
util: add callbackify
Add `util.callbackify(function)` for creating callback style functions from functions returning a `Thenable` PR-URL: https://github.com/nodejs/node/pull/12712 Fixes: https://github.com/nodejs/CTC/issues/109 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Teddy Katz <teddy.katz@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/errors.js1
-rw-r--r--lib/util.js50
2 files changed, 51 insertions, 0 deletions
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 57e73ac56a..1f1dd82c37 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -164,6 +164,7 @@ E('ERR_SOCKET_BAD_PORT', 'Port should be > 0 and < 65536');
E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running');
E('ERR_V8BREAKITERATOR', 'full ICU data not installed. ' +
'See https://github.com/nodejs/node/wiki/Intl');
+E('FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value');
// Add new errors from here...
function invalidArgType(name, expected, actual) {
diff --git a/lib/util.js b/lib/util.js
index 3c31e0932a..11dd521668 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -1047,3 +1047,53 @@ process.versions[exports.inspect.custom] =
(depth) => exports.format(JSON.parse(JSON.stringify(process.versions)));
exports.promisify = internalUtil.promisify;
+
+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) {
+ const newReason = new errors.Error('FALSY_VALUE_REJECTION');
+ newReason.reason = reason;
+ reason = newReason;
+ Error.captureStackTrace(reason, callbackifyOnRejected);
+ }
+ return cb(reason);
+}
+
+
+function callbackify(original) {
+ if (typeof original !== 'function') {
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE',
+ 'original',
+ '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(...args) {
+ const maybeCb = args.pop();
+ if (typeof maybeCb !== 'function') {
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE',
+ 'last argument',
+ 'function');
+ }
+ const cb = (...args) => { Reflect.apply(maybeCb, this, args); };
+ // In true node style we process the callback on `nextTick` with all the
+ // implications (stack, `uncaughtException`, `async_hooks`)
+ Reflect.apply(original, this, args)
+ .then((ret) => process.nextTick(cb, null, ret),
+ (rej) => process.nextTick(callbackifyOnRejected, rej, cb));
+ }
+
+ Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
+ Object.defineProperties(callbackified,
+ Object.getOwnPropertyDescriptors(original));
+ return callbackified;
+}
+
+exports.callbackify = callbackify;