Skip to content
Closed
25 changes: 13 additions & 12 deletions src/debuggability.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";
module.exports = function(Promise, Context) {
var getDomain = Promise._getDomain;
var getContext = Promise._getContext;
var async = Promise._async;
var Warning = require("./errors").Warning;
var util = require("./util");
Expand Down Expand Up @@ -104,19 +104,13 @@ Promise.prototype._warn = function(message, shouldUseOwnTrace, promise) {
};

Promise.onPossiblyUnhandledRejection = function (fn) {
var domain = getDomain();
possiblyUnhandledRejection =
typeof fn === "function" ? (domain === null ?
fn : util.domainBind(domain, fn))
: undefined;
var context = getContext();
possiblyUnhandledRejection = util.contextBind(context, fn);
};

Promise.onUnhandledRejectionHandled = function (fn) {
var domain = getDomain();
unhandledRejectionHandled =
typeof fn === "function" ? (domain === null ?
fn : util.domainBind(domain, fn))
: undefined;
var context = getContext();
unhandledRejectionHandled = util.contextBind(context, fn);
};

var disableLongStackTraces = function() {};
Expand Down Expand Up @@ -308,6 +302,9 @@ Promise.config = function(opts) {
Promise.prototype._fireEvent = defaultFireEvent;
}
}
if ("asyncHooks" in opts) {
config.asyncHooks = opts.asyncHooks;
}
return Promise;
};

Expand Down Expand Up @@ -933,12 +930,16 @@ var config = {
warnings: warnings,
longStackTraces: false,
cancellation: false,
monitoring: false
monitoring: false,
asyncHooks: false
};

if (longStackTraces) Promise.longStackTraces();

return {
asyncHooks: function() {
return config.asyncHooks;
},
longStackTraces: function() {
return config.longStackTraces;
},
Expand Down
8 changes: 3 additions & 5 deletions src/join.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";
module.exports =
function(Promise, PromiseArray, tryConvertToPromise, INTERNAL, async,
getDomain) {
getContext) {
var util = require("./util");
var canEvaluate = util.canEvaluate;
var tryCatch = util.tryCatch;
Expand Down Expand Up @@ -147,10 +147,8 @@ Promise.join = function () {

if (!ret._isFateSealed()) {
if (holder.asyncNeeded) {
var domain = getDomain();
if (domain !== null) {
holder.fn = util.domainBind(domain, holder.fn);
}
var context = getContext();
holder.fn = util.contextBind(context, holder.fn);
}
ret._setAsyncGuaranteed();
ret._setOnCancel(holder);
Expand Down
6 changes: 3 additions & 3 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = function(Promise,
INTERNAL,
debug) {
var ASSERT = require("./assert");
var getDomain = Promise._getDomain;
var getContext = Promise._getContext;
var util = require("./util");
var tryCatch = util.tryCatch;
var errorObj = util.errorObj;
Expand All @@ -15,8 +15,8 @@ var async = Promise._async;
function MappingPromiseArray(promises, fn, limit, _filter) {
this.constructor$(promises);
this._promise._captureStackTrace();
var domain = getDomain();
this._callback = domain === null ? fn : util.domainBind(domain, fn);
var context = getContext();
this._callback = util.contextBind(context, fn);
this._preservedValues = _filter === INTERNAL
? new Array(this.length())
: null;
Expand Down
59 changes: 35 additions & 24 deletions src/promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,32 @@ var UNDEFINED_BINDING = {};
var ASSERT = require("./assert");
var util = require("./util");

var getDomain;
var getContext;
if (util.isNode) {
getDomain = function() {
var ret = process.domain;
if (ret === undefined) ret = null;
return ret;
};
if (util.nodeSupportsAsyncResource) {
var AsyncResource = require("async_hooks").AsyncResource;
Copy link
Copy Markdown

@hybrist hybrist Nov 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this stripped somehow in the browser build? E.g. will this expression make it into js/browser/bluebird.js? If so, it might cause problems while bundling in apps using bluebird. Maybe we'd want to put this logic into a completely separate file that's "guarded" by a file-level browser override so it's excluded from bundling? E.g. something like this:

// src/getContext.browser.js, gets loaded directly in browser environments
module.exports = function getContext() { return {}; }

// src/getContext.js, gets loaded in not-definitely-browser environments
var getFallbackContext = require('./getContext.browser.js');
var getContext;
// [...]

// src/promise.js
var getContext = require('./getContext');

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see the exclude in the browserify instructions, require will just return an empty object in that case (and even that won't be executed due to the isNode check before).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, gotcha. It compiles to _dereq_('async_hooks') which just never gets executed. 👍

getContext = function() {
if (!debug.asyncHooks()) {
return { domain: process.domain };
}
return {
domain: process.domain,
async: new AsyncResource("Bluebird::Promise")
};
};
} else {
getContext = function() {
return {
domain: process.domain
};
};
}
} else {
getDomain = function() {
return null;
getContext = function() {
return {};
};
}
util.notEnumerableProp(Promise, "_getDomain", getDomain);
util.notEnumerableProp(Promise, "_getContext", getContext);

var es5 = require("./es5");
var Async = require("./async");
Expand Down Expand Up @@ -244,7 +257,7 @@ Promise.prototype._then = function (
this._fireEvent("promiseChained", this, promise);
}

var domain = getDomain();
var context = getContext();
if (!BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
var handler, value, settler = target._settlePromiseCtx;
if (BIT_FIELD_CHECK(IS_FULFILLED)) {
Expand All @@ -262,15 +275,14 @@ Promise.prototype._then = function (
}

async.invoke(settler, target, {
handler: domain === null ? handler
: (typeof handler === "function" &&
util.domainBind(domain, handler)),
handler: util.contextBind(context, handler),
promise: promise,
receiver: receiver,
value: value
});
} else {
target._addCallbacks(didFulfill, didReject, promise, receiver, domain);
target._addCallbacks(didFulfill, didReject, promise,
receiver, context);
}

return promise;
Expand Down Expand Up @@ -396,9 +408,9 @@ Promise.prototype._addCallbacks = function (
reject,
promise,
receiver,
domain
context
) {
ASSERT(typeof domain === "object");
ASSERT(typeof context === "object");
ASSERT(!this._isFateSealed());
ASSERT(!this._isFollowing());
var index = this._length();
Expand All @@ -417,12 +429,10 @@ Promise.prototype._addCallbacks = function (
this._promise0 = promise;
this._receiver0 = receiver;
if (typeof fulfill === "function") {
this._fulfillmentHandler0 =
domain === null ? fulfill : util.domainBind(domain, fulfill);
this._fulfillmentHandler0 = util.contextBind(context, fulfill);
}
if (typeof reject === "function") {
this._rejectionHandler0 =
domain === null ? reject : util.domainBind(domain, reject);
this._rejectionHandler0 = util.contextBind(context, reject);
}
} else {
ASSERT(this[base + CALLBACK_PROMISE_OFFSET] === undefined);
Expand All @@ -434,11 +444,11 @@ Promise.prototype._addCallbacks = function (
this[base + CALLBACK_RECEIVER_OFFSET] = receiver;
if (typeof fulfill === "function") {
this[base + CALLBACK_FULFILL_OFFSET] =
domain === null ? fulfill : util.domainBind(domain, fulfill);
util.contextBind(context, fulfill);
}
if (typeof reject === "function") {
this[base + CALLBACK_REJECT_OFFSET] =
domain === null ? reject : util.domainBind(domain, reject);
util.contextBind(context, reject);
}
}
this._setLength(index + 1);
Expand Down Expand Up @@ -590,7 +600,8 @@ Promise.prototype._settlePromise = function(promise, handler, receiver, value) {
if (tryCatch(handler).call(receiver, value) === errorObj) {
promise._reject(errorObj.e);
}
} else if (handler === reflectHandler) {
} else if (handler === reflectHandler || (handler &&
handler[util.wrappedSymbol] === reflectHandler)) {
promise._fulfill(reflectHandler.call(receiver));
} else if (receiver instanceof Proxyable) {
receiver._promiseCancelled(promise);
Expand Down Expand Up @@ -786,7 +797,7 @@ require("./cancel")(Promise, PromiseArray, apiRejection, debug);
require("./direct_resolve")(Promise);
require("./synchronous_inspection")(Promise);
require("./join")(
Promise, PromiseArray, tryConvertToPromise, INTERNAL, async, getDomain);
Promise, PromiseArray, tryConvertToPromise, INTERNAL, async, getContext);
Promise.Promise = Promise;
Promise.version = "__VERSION__";
};
10 changes: 5 additions & 5 deletions src/reduce.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ module.exports = function(Promise,
tryConvertToPromise,
INTERNAL,
debug) {
var getDomain = Promise._getDomain;
var getContext = Promise._getContext;
var util = require("./util");
var tryCatch = util.tryCatch;

function ReductionPromiseArray(promises, fn, initialValue, _each) {
this.constructor$(promises);
var domain = getDomain();
this._fn = domain === null ? fn : util.domainBind(domain, fn);
var context = getContext();
this._fn = util.contextBind(context, fn);
if (initialValue !== undefined) {
initialValue = Promise.resolve(initialValue);
initialValue._attachCancellationCallback(this);
Expand All @@ -32,8 +32,8 @@ function ReductionPromiseArray(promises, fn, initialValue, _each) {
util.inherits(ReductionPromiseArray, PromiseArray);

ReductionPromiseArray.prototype._gotAccum = function(accum) {
if (this._eachValues !== undefined &&
this._eachValues !== null &&
if (this._eachValues !== undefined &&
this._eachValues !== null &&
accum !== INTERNAL) {
this._eachValues.push(accum);
}
Expand Down
33 changes: 29 additions & 4 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,25 @@ function getNativePromise() {
}
}

function domainBind(self, cb) {
return self.bind(cb);
function contextBind(ctx, cb) {
if (typeof cb !== "function")
return cb;

if (ctx != null && ctx.domain != null) {
cb = ctx.domain.bind(cb);
}

if (ctx != null && ctx.async != null) {
var old = cb;
cb = function() {
INLINE_SLICE(args, arguments);
return ctx.async.runInAsyncScope(function() {
return old.apply(this, args);
}, this);
};
cb[ret.wrappedSymbol] = old;
}
return cb;
}

var ret = {
Expand Down Expand Up @@ -382,17 +399,25 @@ var ret = {
env: env,
global: globalObject,
getNativePromise: getNativePromise,
domainBind: domainBind
contextBind: contextBind
};
ret.isRecentNode = ret.isNode && (function() {
var version;
if (process.versions && process.versions.node) {
if (process.versions && process.versions.node) {
version = process.versions.node.split(".").map(Number);
} else if (process.version) {
version = process.version.split(".").map(Number);
}
return (version[0] === 0 && version[1] > 10) || (version[0] > 0);
})();
ret.nodeSupportsAsyncResource = ret.isNode && (function() {
var version = process.versions.node.split(".").map(Number);
return (version[0] === 9 && version[1] >= 6) || (version[0] > 9);
})();

if (ret.nodeSupportsAsyncResource) {
ret.wrappedSymbol = Symbol();
}

if (ret.isNode) ret.toFastProperties(process);

Expand Down
Loading