Comparing version 2.4.0-3 to 3.0.0-0
@@ -221,2 +221,3 @@ /** | ||
methodName === '_hasFinishedExecuting' || | ||
methodName === '_hasAlreadyWaitedAtLeastOneTick' || | ||
methodName === '_hasTimedOut' || | ||
@@ -223,0 +224,0 @@ methodName === '_handleExec' || |
@@ -152,26 +152,9 @@ /** | ||
// This variable (`cb`) is used as our callback. In the next few lines, we determine | ||
// what it will be. This is just a potentially-more-efficient alternative to a series | ||
// of self-calling functions that we use to afford better performance in the general case. | ||
// (Normally, this sort of micro-optimization wouldn't matter, but this is an extradordinarily | ||
// hot code path. Note that if we can prove self-calling functions are just as good, or even | ||
// good enough, it would be preferable to use them instead (not only for consistency, but | ||
// certainly for clarity as well).) | ||
var cb; | ||
// Impose arbitrary restriction against an edge case that would hurt performance for us to implement. | ||
// FUTURE: maybe solve this, but see note below -- might not even be relevant... | ||
if (self._interceptAfterExec && !_.isUndefined(handleUncaughtException)){ | ||
throw new Error('Consistency violation: Currently, the 2nd argument to .exec() may not be used, since this Deferred was built with a custom `interceptAfterExec` handler. Please avoid using the 2nd argument to .exec() and use ES8 async/await instead, if possible.'); | ||
} | ||
// If 2nd argument (handleUncaughtException) was provided, then wrap `_cb` in another function | ||
// that protects against uncaught exceptions. | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// FUTURE: Consider adding a deprecation notice to this behavior and eventually removing | ||
// support (now that ES8 async/await widely available on the server, and should be available | ||
// in the browser... soonish?) | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
if (!_.isUndefined(handleUncaughtException)) { | ||
// Check usage of 2nd argument to .exec(). | ||
if (handleUncaughtException !== undefined) { | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// FUTURE: Consider adding a deprecation notice to this behavior, and then remove support | ||
// altogether. (This isn't really as necessary anymore now that ES8 async/await is widely | ||
// available on the server, and should be available in the browser... soonish?) | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
if (!_.isFunction(handleUncaughtException)) { | ||
@@ -186,48 +169,133 @@ throw flaverr({ | ||
}, self._omen); | ||
}//-• | ||
}//• | ||
cb = function _tryToRunCb() { | ||
// Impose arbitrary restriction against an unsupported edge case. | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// FUTURE: maybe remove this restriction. But also... might not even be relevant, since | ||
// we'll probably get rid of support for this usage (see above) | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
if (self._interceptAfterExec){ | ||
throw new Error('Consistency violation: Currently, the 2nd argument to .exec() may not be used, since this Deferred was built with a custom `interceptAfterExec` handler. Please avoid using the 2nd argument to .exec() and use ES8 async/await instead, if possible.'); | ||
}//• | ||
}//fi | ||
// This variable (`cb`) is used as our callback. In the next few lines, we determine | ||
// what it will be. This is just a potentially-more-efficient alternative to a series | ||
// of self-calling functions, We only do this to afford better performance in the general case. | ||
// | ||
// > (Normally, this sort of micro-optimization wouldn't matter, but this is an extradordinarily | ||
// > hot code path. Note that if we can prove self-calling functions are just as good, or even | ||
// > good enough, it would be preferable to use them instead (not only for consistency, but | ||
// > certainly for clarity as well).) | ||
var cb; | ||
// To begin with, no matter what, intercept `_cb` by wrapping it in another function | ||
// (a new one that we'll call `cb`) which adds some additional checks. | ||
// | ||
// > Note that we don't use .slice() on the `arguments` keyword -- this is for perf. | ||
// > (see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage) | ||
cb = function _tryToRunCb(/*…*/) { | ||
// If this Deferred was built with an `interceptAfterExec` lifecycle callback, | ||
// then intercept our normal flow to call that lifecycle callback, picking up | ||
// the potentially-changed (even potentially-reconstructed!) error or result. | ||
if (self._interceptAfterExec) { | ||
if (arguments[0]) { | ||
arguments[0] = self._interceptAfterExec(arguments[0]); | ||
} | ||
else { | ||
arguments[1] = self._interceptAfterExec(undefined, arguments[1]); | ||
} | ||
}//fi | ||
// If this callback is being called after at least one tick has elapsed... | ||
if (self._hasAlreadyWaitedAtLeastOneTick) { | ||
// If 2nd argument (handleUncaughtException) was provided to .exec(), then run that | ||
// instead of throwing. This protects against unexpected, uncaught exceptions in | ||
// asynchronous callbacks. | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// FUTURE: Probably deprecate this, then remove support (see above). | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
if (handleUncaughtException) { | ||
try { | ||
return _cb.apply(undefined, arguments); | ||
} catch (unexpectedErrorFromCallback) { | ||
return handleUncaughtException(unexpectedErrorFromCallback); | ||
} | ||
}//• | ||
// Otherwise, just trigger the callback as-is. | ||
// (If it throws, it will crash the process!) | ||
return _cb.apply(undefined, arguments); | ||
}//• | ||
//‡ | ||
// Otherwise, our logic is synchronous (i.e. <1 async tick has elapsed at the time it's being | ||
// called). So wrap the `_cb` from userland in a try/catch. If an unhandled error of any kind | ||
// is thrown from the userland cb, our wrapper uses a special Envelope to bust out of the `try` | ||
// block, ensuring that the unhandled exception is thrown up to userland. | ||
// | ||
// > NOTE: | ||
// > Without this extra code here, we'd end up with the old behavior: outputting a puzzling error | ||
// > message -- e.g. about something unexpected things happening in the Deferred, or a warning | ||
// > about triggering the callback twice (when actually, the issue is that something went wrong | ||
// > in the callback-- and that the Deferred logic happened to be synchronous, so it wasn't able | ||
// > to escape parley's internal `try` block.) | ||
// > | ||
// > Some relevant links for reference: | ||
// > • https://github.com/node-machine/machine/blob/7fdcf8a869605d0951909725061379cd27bd7f0d/lib/private/intercept-exit-callbacks.js#L186-L238 | ||
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_hasFnYieldedYet&type= | ||
// > • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_runningSynchronously&type= | ||
else { | ||
try { | ||
// > Note that we don't use .slice() -- this is for perf. | ||
// > (see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#what-is-safe-arguments-usage) | ||
return _cb.apply(undefined, arguments); | ||
} catch (e) { return handleUncaughtException(e); } | ||
}; | ||
}//‡ | ||
// If this Deferred was built with an `interceptAfterExec` lifecycle callback, then wrap | ||
// `_cb` in another function that intercepts the error or result. | ||
else if (self._interceptAfterExec){ | ||
cb = function _performInterceptAfterExec(err, result){ | ||
if (err) { | ||
err = self._interceptAfterExec(err); | ||
return _cb(err); | ||
} catch (unexpectedErrorFromCallback) { | ||
throw flaverr({ | ||
name: 'Envelope', | ||
code: 'E_ESCAPE_HATCH', | ||
traceRef: self, | ||
raw: unexpectedErrorFromCallback | ||
}); | ||
} | ||
else { | ||
result = self._interceptAfterExec(undefined, result); | ||
return _cb(undefined, result); | ||
} | ||
}; | ||
}//‡ | ||
// Otherwise, just use `_cb` as-is. | ||
else { | ||
}//• | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// FUTURE: Consider wrapping cb in a function that injects a try/catch. If an unhandled | ||
// exception is thrown, our wrapper could check synchronicity and, if synchronous, inject | ||
// a `setImmediate()` call to ensure that the process crashes from an unhandled exception, | ||
// rather than outputting a rather puzzling error message -- e.g. about something unexpected | ||
// happening in the Deferred (when actually, the issue is that something went wrong in the | ||
// callback-- and that the Deferred logic happened to be synchronous.) | ||
// FUTURE: Additionally, this additional layer of wrapping could take care of improving | ||
// stack traces, even in the case where an Error comes up from inside the implementation. | ||
// If done carefully, this can be done in a way that protects characteristics of the | ||
// internal Error (e.g. its "code", etc.), while also providing a better stack trace. | ||
// | ||
// This would take care of weird errors that have stuff like this in them: | ||
// "Also, here are the available keys on `self` at this point:\n```\n_handleExec,meta,usingConnection,where,limit,skip,paginate,sort,sum,avg,min,max,groupBy,select,omit,populateAll,populate,_WLModel,_wlQueryInfo,_hasBegunExecuting,_hasFinishedExecuting" | ||
// | ||
// Some relevant links: | ||
// • https://github.com/node-machine/machine/blob/7fdcf8a869605d0951909725061379cd27bd7f0d/lib/private/intercept-exit-callbacks.js#L186-L238 | ||
// • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_hasFnYieldedYet&type= | ||
// • https://github.com/node-machine/machine/search?utf8=%E2%9C%93&q=_runningSynchronously&type= | ||
// For example, something like this: | ||
// ``` | ||
// var relevantPropNames = _.difference( | ||
// _.union( | ||
// ['name', 'message'], | ||
// Object.getOwnPropertyNames(underlyingError) | ||
// ), | ||
// ['stack'] | ||
// ); | ||
// var errTemplate = _.pick(underlyingError, relevantPropNames); | ||
// errTemplate.raw = underlyingError;//<< could override stuff-- that's ok (see below). | ||
// var newError = flaverr(errTemplate, omen); | ||
// ``` | ||
// > Note that, above, we also kept the original error (and thus _its_ trace) and | ||
// > attached that as a separate property. If the original error already has "raw", | ||
// > that's ok. This is one thing that it makes sense for us to mutate-- and any | ||
// > attempt to do otherwise would probably be more confusing (you can imagine a while | ||
// > loop where we add underscores in front of the string "raw", and use that as a keyname. | ||
// > But again, that ends up being more confusing from a userland perspective.) | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
cb = _cb; | ||
}//>- | ||
};//ƒ | ||
// Userland spinlock | ||
@@ -306,3 +374,3 @@ if (self._hasBegunExecuting) { | ||
}, self._timeout);// _∏_ | ||
}, self._timeout);// _∏_ (invoking `setTimeout()`) | ||
}//>- | ||
@@ -312,6 +380,6 @@ | ||
// Trigger the executioner function. | ||
// | ||
// > Note that we always wrap the executioner in a `try` block to prevent common issues from | ||
// > uncaught exceptions, at least within the tick. | ||
try { | ||
self._handleExec(function (err, result) { | ||
@@ -378,5 +446,9 @@ | ||
self._hasFinishedExecuting = true; | ||
return cb(err); | ||
}//-• | ||
// IWMIH, there was no error. | ||
self._hasFinishedExecuting = true; | ||
@@ -408,2 +480,10 @@ | ||
// Check to make sure this error isn't a special "escape hatch" from | ||
// the edge case where an error was thrown from within the userland callback | ||
// provided to .exec() -- specifically in the case where the handleExec logic | ||
// is synchronous (i.e. non-blocking- triggering its `done` within 1 tick.) | ||
if (e.name === 'Envelope' && e.code === 'E_ESCAPE_HATCH' && e.traceRef === self) { | ||
throw e.raw; | ||
}//• | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
@@ -497,2 +577,17 @@ // NOTE: The following code is ALMOST exactly the same as the code above. | ||
}//</catch> | ||
// Use `self._hasAlreadyWaitedAtLeastOneTick` to track whether or not this logic is asynchronous. | ||
if (self._hasFinishedExecuting) { | ||
// IWMIH and we've already finished running `handleExec`, then we know | ||
// it must have been composed purely of blocking (i.e. synchronous) logic. | ||
self._hasAlreadyWaitedAtLeastOneTick = false; | ||
} | ||
else { | ||
// Otherwise, IWMIH we know that the callback hasn't been called yet-- meaning | ||
// that we're likely dealing with some non-blocking (i.e. asynchronous) logic. | ||
// (Or possibly a bug where the callback isn't getting called -_-) | ||
self._hasAlreadyWaitedAtLeastOneTick = true; | ||
} | ||
}; | ||
@@ -499,0 +594,0 @@ |
{ | ||
"name": "parley", | ||
"version": "2.4.0-3", | ||
"version": "3.0.0-0", | ||
"description": "Practical, lightweight flow control for Node.js. Supports callbacks and promises.", | ||
@@ -5,0 +5,0 @@ "main": "lib/parley.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
170714
2491