Comparing version 1.0.1 to 1.0.2
@@ -9,2 +9,3 @@ /** | ||
var bluebird = require('bluebird'); | ||
var Deferred = require('./private/Deferred'); | ||
@@ -92,2 +93,13 @@ | ||
// | ||
// ================================================================== | ||
// Note: to temporarily switch to the prototype/constructor strategy, | ||
// comment out the inline dictionary definition below in favor of | ||
// this line: (i.e. just uncomment it) | ||
// ``` | ||
// var π = new Deferred(opts.codeName, opts.handleExec); | ||
// ``` | ||
// For more info & benchmarks, see: | ||
// https://github.com/mikermcneil/parley/commit/5996651c4b15c7850b5eb2e4dc038e8202414553#commitcomment-20256030 | ||
// ================================================================== | ||
// Build deferred object. | ||
@@ -94,0 +106,0 @@ var π = { |
{ | ||
"name": "parley", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "Practical, lightweight flow control for Node.js. Supports callbacks and promises.", | ||
@@ -5,0 +5,0 @@ "main": "lib/parley.js", |
@@ -13,3 +13,3 @@ parley | ||
doStuff({ foo: 123 }) | ||
.set({ bar: 456 }) | ||
.foo({ bar: 456 }) | ||
.exec(function (err, result){ | ||
@@ -24,3 +24,3 @@ | ||
doStuff({ foo: 123 }) | ||
.set({ bar: 456 }) | ||
.baz({ bar: 456 }) | ||
.then(function (result){ | ||
@@ -27,0 +27,0 @@ |
@@ -48,3 +48,3 @@ /** | ||
// | ||
// Dec 20, 2016 (take 4): (after removing pretty-print) | ||
// Dec 20, 2016 (take 4): (after removing pretty-print, BEFORE switching to the constructor approach) | ||
// ================================================================================================================ | ||
@@ -55,3 +55,3 @@ // baseline.benchmark.js | ||
// • b e n c h m a r k s • | ||
// • (instantiation) ° | ||
// • ° | ||
// ------------------------------------ | ||
@@ -89,8 +89,75 @@ // parley(handler) | ||
// | ||
// • The additional time added by calling .exec() (vs. just building) is actually a NEGATIVE number | ||
// in some cases. i.e. calling .exec() does not add any noticeable latency. | ||
// • The additional time added by calling .exec() (vs. just building) is really only | ||
// visible now, AFTER removing pretty-print. It's a difference of 100,000 ops/sec. | ||
// | ||
// • By itself, switching to a Deferred constructor doesn't really improve performance | ||
// by THAT much. In some cases, it actually makes it worse (e.g. consistent decrease | ||
// in ops/sec for the first 2 benchmarks: just_build, build_and_exec). BUT it does | ||
// ever-so-slightly increase performance for both mock "find" mock "validate". | ||
// The question is: "why?" My guess is that it has something to do w/ accessing | ||
// `this` being slower than closure scope, and thus outweighing the gains of faster | ||
// construction. But even then, that wouldn't explain "just_build" being slower, so | ||
// it's probably not that... | ||
// | ||
// • Reducing the number of `this`/`self` references did not seem to make any sort of | ||
// meaningful difference on performance. (see 1d8b6239de2cd84ac76ee015d099c3c5a7013989) | ||
// *******UPDATE*******: | ||
// Actually -- it might... after removing two unncesssary `this` assignments from the | ||
// CONSTRUCTOR itself, performance for "just_build" shot up to where it was for the | ||
// original closure approach (and possibly a bit higher). Still, this is negligible | ||
// at the moment, but it's probably an effect that is more pronounced when overall ops/sec | ||
// are orders of magnitude higher (e.g. in the millions instead of the hundreds of thousands.) | ||
// Even then-- this is still less important than one might expect! | ||
// | ||
// • Aside: using a standalone function declaration (rather than invoking a self-calling function) | ||
// increases performance, like you might expect. Whether it's enough to matter is probably | ||
// situational. In the case of the commit where this observation was added to the code base, | ||
// it made a difference of ~1,000,000 ops/sec for the "NAKED mock validate" benchmark, and a | ||
// difference of ~20,000 ops/sec for the "validate w/ .exec()" benchmark. Worth it...? | ||
// No. Inline function declarations are NEVER worth it. But in some cases it might be worthwhile | ||
// to pull out shared futures used by self-invoking functions and drop them into a separate module. | ||
// *******UPDATE*******: | ||
// Just verified that, by moving the inline function to a separate file, performance for the | ||
// "NAKED mock validate" went up by an ADDITIONAL 2,000,000 ops/sec, and by an ADDITIONAL | ||
// ~20,000 ops/sec for the "validate w/ .exec()" benchmark. So, in conclusion, the answer to the | ||
// question of "Worth it?" is a resounding YES -- but only for a hot code path like this. For | ||
// other bits of code, the advantages of keeping the logic inline and avoiding a separate, | ||
// weirdly-specific file, are well worth it. And as for INLINE named function declarations? | ||
// They're still never worth it. Not only do they clutter the local scope and create scoffable | ||
// confusion about flow control (plus all the resulting bug potential), they aren't even as fast | ||
// as pulling out the code into a separate file. (Presumably this is because V8 has to make sure | ||
// the inline function can access the closure scope.) | ||
// | ||
// • It is worth noting that, despite how exciting the previous notes about pulling out self-invoking | ||
// functions was, when attempted with the mock "find" fixture, the relevant benchmarks showed no | ||
// noticeable improvement (i.e. because they're doing something asynchronous.) | ||
// | ||
// • Swapping out non-standard variable names (e.g. π) did not have any noticeable effect. | ||
// | ||
// • When using the constructor+prototype approach, accessing `this` is slow. It's not THAT bad, | ||
// but it is definitely a thing. Note that it is somewhat worse if in the constructor-- and | ||
// also worse on assignment (this.foo = x) than on access (var x = this.foo). | ||
// | ||
// • When using the closure approach, adding new methods dynamically is slow. This doesn't seem | ||
// to be because defining new functions is slow, per se. Rather it seems to have to do with | ||
// mutating the object after it's already been created. As a middle ground, it seems that relying | ||
// on Lodash's built-in optimizations is the way to go. Simply changing from `deferred.meta = ...` | ||
// to `_.extend(deferred, { meta: ... })` split the difference as far as performance. It improved | ||
// the performance of the 'mock validate with .exec()' benchmark by ~50k-60k ops/sec; i.e. ~20%) | ||
// | ||
// • STILL BE CAREFUL when using the closure approach. Even with the _.extend() trick, performance | ||
// decreases as more and more methods are added, whether or not they're within the same `.extend()` | ||
// call. BUT: What's still unclear is if this is due to function construction, or something else. | ||
// In this case, in practice, tricks would need to be used to circumvent the need for closure scope | ||
// access (i.e. prbly .bind()). But the answer to the question can actualy be figured out regardless-- | ||
// by defining stub functions once per process. | ||
// *******UPDATE*******: | ||
// Well, the answer is that the function construction must have mattered somewhat, but even after | ||
// pulling the entire dictionary of methods out (and pretending they're static), the performance is | ||
// still lower than when _.extend() is used to attach only ONE method-- even when that one method is | ||
// defined inline. So, at the end of the day, we're just going to have to deal with the fact that, | ||
// if we add methods to the Deferred dynamically and construction-time, it's going to be slower and | ||
// slower for every additional method we add. | ||
// ╔═╗╦ ╦╦╔╦╗╔═╗ | ||
@@ -103,3 +170,2 @@ // ╚═╗║ ║║ ║ ║╣ | ||
// •~ between 21,000 and 23,500 ops/sec (Dec 20, 2016) | ||
function just_build(){ | ||
@@ -117,5 +183,2 @@ var deferred = parley(function(handlerCb) { return handlerCb(); }); | ||
// •~ between 21,000 and 23,000 ops/sec (Dec 20, 2016) | ||
// --SAME AS ABOVE, basically -- like 200-500 ops/second slower, tops -- | ||
// (and keep in mind that's below the % error margin) | ||
function build_AND_exec(){ | ||
@@ -122,0 +185,0 @@ var deferred = parley(function(handlerCb) { return handlerCb(); }); |
@@ -8,5 +8,5 @@ /** | ||
var parley = require('../../'); | ||
var helpFind = require('./private/help-find.util'); | ||
/** | ||
@@ -54,44 +54,11 @@ * find.fixture.js | ||
// so we'll build and return a Deferred instead. | ||
(function _determineFinalCb(proceed){ | ||
if (!_.isUndefined(explicitCb)) { | ||
proceed(undefined, explicitCb); | ||
} | ||
else { | ||
deferred = parley(function (deferredCb){ | ||
proceed(undefined, deferredCb); | ||
}); | ||
} | ||
// ~∞%° | ||
})(function (unused, finalCb) { | ||
if (unused) { | ||
finalCb(new Error('Consistency violation: Unexpected internal error occurred before beginning with any business logic. Details: '+unused.stack)); | ||
return; | ||
}//-• | ||
if (!_.isUndefined(explicitCb)) { | ||
helpFind(undefined, metadata, explicitCb); | ||
} | ||
else { | ||
deferred = parley(function (deferredCb){ | ||
helpFind(undefined, metadata, deferredCb); | ||
}); | ||
}//>- | ||
// Now actually do stuff. | ||
// In this case, we'll just pretend, since this part doesn't matter. | ||
// (we just wait a few miliseconds, and then send back an array consisting | ||
// of one item: the `criteria` that was received.) | ||
setTimeout(function (){ | ||
var fakeResult = [ metadata.criteria ]; | ||
// Note that, as a way for our test cases to instrument the outcome, | ||
// we check `metadata.criteria` here, and if it happens to be `false` | ||
// or `null`, then we trigger an error instead. | ||
if (metadata.criteria === false) { | ||
return finalCb(flaverr('E_SOME_ERROR', new Error('Simulated failure (E_SOME_ERROR)'))); | ||
} | ||
if (_.isNull(metadata.criteria)) { | ||
return finalCb(new Error('Simulated failure (catchall / misc. error)')); | ||
} | ||
return finalCb(undefined, fakeResult); | ||
}, 25); | ||
});//</ self-invoking function> | ||
// If we ended up building a Deferred above, we would have done so synchronously. | ||
@@ -111,7 +78,9 @@ // In other words, if there's going to be a Deferred, we have it here. | ||
// At this point, we might opt to attach some methods to our Deferred. | ||
deferred.where = function (clause){ | ||
metadata.criteria = metadata.criteria || {}; | ||
metadata.criteria.where = clause; | ||
return deferred; | ||
}; | ||
_.extend(deferred, { | ||
where: function(clause) { | ||
metadata.criteria = metadata.criteria || {}; | ||
metadata.criteria.where = clause; | ||
return deferred; | ||
} | ||
}); | ||
@@ -118,0 +87,0 @@ // When we're confident that our Deferred is ready for primetime, |
@@ -8,5 +8,5 @@ /** | ||
var parley = require('../../'); | ||
var helpValidate = require('./private/help-validate.util'); | ||
/** | ||
@@ -45,28 +45,12 @@ * validate.fixture.js | ||
// so we'll build and return a Deferred instead. | ||
(function _determineFinalCb(proceed){ | ||
if (!_.isUndefined(explicitCb)) { | ||
proceed(undefined, explicitCb); | ||
} | ||
else { | ||
deferred = parley(function (deferredCb){ | ||
proceed(undefined, deferredCb); | ||
}); | ||
} | ||
// ~∞%° | ||
})(function (unused, finalCb) { | ||
if (unused) { | ||
finalCb(new Error('Consistency violation: Unexpected internal error occurred before beginning with any business logic. Details: '+unused.stack)); | ||
return; | ||
}//-• | ||
if (!_.isUndefined(explicitCb)) { | ||
helpValidate(undefined, explicitCb); | ||
} | ||
else { | ||
deferred = parley(function (deferredCb){ | ||
helpValidate(undefined, deferredCb); | ||
}); | ||
}//>- | ||
// Now actually do stuff. | ||
// ...except actually don't-- this is just pretend. | ||
// All done. | ||
return finalCb(); | ||
});//</ self-invoking function> | ||
// If we ended up building a Deferred above, we would have done so synchronously. | ||
@@ -86,6 +70,53 @@ // In other words, if there's going to be a Deferred, we have it here. | ||
// At this point, we might opt to attach some methods to our Deferred. | ||
deferred.meta = function (_meta){ | ||
metadata.meta = _meta; | ||
return deferred; | ||
}; | ||
// --(1)------------------------------------------------------- | ||
// --too slow: | ||
// --(e.g. 212k ops/sec) | ||
// deferred.meta = function (_meta){ | ||
// metadata.meta = _meta; | ||
// return deferred; | ||
// }; | ||
// --(2)------------------------------------------------------- | ||
// --perfectly fast, but doesn't do anything: | ||
// --(e.g. 373k ops/sec) | ||
// var theMeta = function (_meta){ | ||
// metadata.meta = _meta; | ||
// return deferred; | ||
// }; | ||
// --(3)------------------------------------------------------- | ||
// --somewhat better than the original!!... | ||
// --(e.g. 273k ops/sec) | ||
// --....but problematic, because it doesn't actually mutate | ||
// --the original deferred, which could cause inconsistencies. | ||
// deferred = _.extend({ | ||
// meta: function (_meta){ | ||
// metadata.meta = _meta; | ||
// return deferred; | ||
// } | ||
// }, deferred); | ||
// --(4)------------------------------------------------------- | ||
// --considerably better than the original!! | ||
// --(Even more than #3... plus it's totally valid!) | ||
// --(e.g. ~268k-292k ops/sec) | ||
_.extend(deferred, { | ||
meta: function (_meta){ | ||
metadata.meta = _meta; | ||
return deferred; | ||
}, | ||
// Uncomment these methods for testing performance: | ||
// (this function gets slower and slower the more you add dynamically like this) | ||
// ================================================================================================ | ||
// a: function (beep, boop) { console.log(Math.random()+'hi0'); return deferred; }, | ||
// b: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi1'); return deferred; }, | ||
// c: function (beep, boop) { console.log(Math.random()+'hi2'); return deferred; }, | ||
// d: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi3'); return deferred; }, | ||
// e: function (beep, boop) { console.log(Math.random()+'hi5'); return deferred; }, | ||
// f: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi5'); return deferred; }, | ||
// g: function (beep, boop) { console.log(Math.random()+'hi6'); return deferred; }, | ||
// h: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi7'); return deferred; }, | ||
// i: function (beep, boop) { console.log(Math.random()+'hi8'); return deferred; }, | ||
// j: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi9'); return deferred; }, | ||
// k: function (beep, boop) { console.log(Math.random()+'hi10'); return deferred; }, | ||
// l: function (baa, baaa, black, sheep) { console.log(Math.random()+'hi11'); return deferred; }, | ||
// ================================================================================================ | ||
}); | ||
@@ -97,1 +128,2 @@ // When we're confident that our Deferred is ready for primetime, | ||
}; | ||
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
82328
19
1293