+13
-0
@@ -0,1 +1,14 @@ | ||
| # prfun 2.1.3 (2015-12-11) | ||
| * Optimize the `Promise` subclass constructor to avoid costly overhead | ||
| in ES5 environments. The slow ES6 path is only used if necessary | ||
| for correctness (or if the native Promise implementation uses ES6 | ||
| class syntax). | ||
| * Added `Promise#then0` to the API, which is a shim when a native | ||
| `Promise#then0` is not available. Some `Promise` implementations | ||
| provide this method, which is much more efficient than calling | ||
| `Promise#then` and discarding the result. | ||
| * Used `Promise#then0` in internal implementations where appropriate, | ||
| including `Promise.async` (where the use of generators can now yield | ||
| better performance than chaining promises in the usual way). | ||
| # prfun 2.1.2 (2015-11-20) | ||
@@ -2,0 +15,0 @@ * Ensure that `Promise.async` always returns a `Promise`. |
+210
-88
@@ -32,28 +32,72 @@ // Utility functions for ES6 Promises. | ||
| // Create a new Promise subclass (this is less cumbersome in es6!) | ||
| var PrFunPromise = function PrFunPromise(exec) { | ||
| var self = new ParentPromise(exec); | ||
| setPrototypeOf(self, PrFunPromise.prototype); | ||
| self._promiseConstructor = PrFunPromise; | ||
| return self; | ||
| }; | ||
| setPrototypeOf(PrFunPromise, ParentPromise); | ||
| PrFunPromise.prototype = Object.create(ParentPromise.prototype); | ||
| PrFunPromise.prototype.constructor = PrFunPromise; | ||
| // This isn't quite right: the way we are creating the subclass | ||
| // above doesn't set the internal [[PromiseConstructor]] field, | ||
| // so we need to tweak the implementation of Promise.resolve() | ||
| var makeResolve = function(parentResolve) { | ||
| return function(x) { | ||
| if (x && typeof x === 'object' && x._promiseConstructor) { | ||
| if (this === x._promiseConstructor) { | ||
| return x; | ||
| } else { | ||
| var makeResolve = null; | ||
| var PrFunPromise = (function makeSubclass(ParentPromise) { | ||
| var PrFunPromise; | ||
| // jscs:disable maximumLineLength | ||
| var isClass = function isClass(v) { | ||
| // See: http://stackoverflow.com/questions/30758961/how-to-check-if-a-variable-is-an-es6-class-declaration | ||
| // And: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-function.prototype.tostring | ||
| return typeof v === 'function' && /^\s*class\s+/.test(v.toString()); | ||
| }; | ||
| // jscs:enable maximumLineLength | ||
| if (isClass(ParentPromise)) { | ||
| // ES6 classes are currently unoptimized in V8. | ||
| // So let's not use them unless ParentPromise does. | ||
| try { | ||
| /* jshint evil:true */ | ||
| return eval('(function(ParentPromise){' + | ||
| '"use strict";' + | ||
| 'return class PrFunPromise extends ParentPromise {};' + | ||
| '})')(ParentPromise); | ||
| } catch (e) { /* I guess true ES6 classes are not supported. */ } | ||
| } | ||
| // Try the "ES5 way"; this is fastest on ES5 engines. | ||
| // (Faster even than ES6 classes, at least on node 5.x, but we assume | ||
| // that the performance of native classes will catch up eventually.) | ||
| PrFunPromise = function PrFunPromise(resolver) { | ||
| ParentPromise.call(this, resolver); | ||
| }; | ||
| setPrototypeOf(PrFunPromise, ParentPromise); | ||
| PrFunPromise.prototype = Object.create(ParentPromise.prototype); | ||
| PrFunPromise.prototype.constructor = PrFunPromise; | ||
| // Try it out first. | ||
| try { | ||
| PrFunPromise.resolve(5); | ||
| // Some native promise implementations will object to the "ES5 way". | ||
| // If they didn't, then let's go with this version. | ||
| return PrFunPromise; | ||
| } catch (e) { /* I guess we'll use the "real" ES6-compatible way. */ } | ||
| PrFunPromise = function PrFunPromise(exec) { | ||
| var self = new ParentPromise(exec); | ||
| setPrototypeOf(self, PrFunPromise.prototype); | ||
| self._promiseConstructor = PrFunPromise; | ||
| return self; | ||
| }; | ||
| setPrototypeOf(PrFunPromise, ParentPromise); | ||
| PrFunPromise.prototype = Object.create(ParentPromise.prototype); | ||
| PrFunPromise.prototype.constructor = PrFunPromise; | ||
| // This isn't quite right: the way we are creating the subclass | ||
| // above doesn't set the internal [[PromiseConstructor]] field, | ||
| // so we need to tweak the implementation of Promise.resolve() | ||
| // (Note that [[PromiseConstructor]] has been removed from the | ||
| // latest draft of the ES6 spec, but it may still be present in | ||
| // your (buggy) native Promise implementation.) | ||
| makeResolve = function(parentResolve) { | ||
| return function(x) { | ||
| if (x && typeof x === 'object' && x._promiseConstructor) { | ||
| if (this === x._promiseConstructor) { | ||
| return x; | ||
| } | ||
| return new this(function(r) { r(x); }); | ||
| } | ||
| } | ||
| return parentResolve.call(this, x); | ||
| return parentResolve.call(this, x); | ||
| }; | ||
| }; | ||
| }; | ||
| PrFunPromise.resolve = makeResolve(PrFunPromise.resolve); | ||
| return PrFunPromise; | ||
| })(ParentPromise); | ||
| if (makeResolve) { | ||
| PrFunPromise.resolve = makeResolve(PrFunPromise.resolve); | ||
| } | ||
| // Sometimes we just need to smash things (sigh) | ||
@@ -72,2 +116,14 @@ var Promise = smash ? ParentPromise : PrFunPromise; | ||
| // ---------- then0 optimization ------- | ||
| // Certain promise implementations (ie, babybird) provide an implementation | ||
| // of `then` which does not return a value. This can be much faster than | ||
| // the native `then`. Shim it if not provided natively. | ||
| if (!Promise.prototype.then0) { | ||
| Promise.prototype.then0 = function(f,r) { this.then(f, r); }; | ||
| } | ||
| // Marker property, to indicate that we don't do anything unsafe | ||
| // in our constructor. | ||
| Promise.noSideEffects = true; | ||
| // ---------- collections -------------- | ||
@@ -89,3 +145,6 @@ | ||
| // as an iterable | ||
| var args = Array.prototype.slice.call(arguments); | ||
| var args = new Array(arguments.length); | ||
| for (var i = 0; i < args.length; ++i) { | ||
| args[i] = arguments[i]; | ||
| } | ||
| return P.all(args); | ||
@@ -193,5 +252,4 @@ }; | ||
| return P.reduce(this, callback); | ||
| } else { | ||
| return P.reduce(this, callback, arguments[1]); | ||
| } | ||
| return P.reduce(this, callback, arguments[1]); | ||
| }; | ||
@@ -206,8 +264,7 @@ | ||
| }); | ||
| } else { | ||
| var initialValue = P.resolve(arguments[2]); | ||
| return P.resolve(pArray).then(function(arr) { | ||
| return arrayReduce.call(arr, reducer(P, callback), initialValue); | ||
| }); | ||
| } | ||
| var initialValue = P.resolve(arguments[2]); | ||
| return P.resolve(pArray).then(function(arr) { | ||
| return arrayReduce.call(arr, reducer(P, callback), initialValue); | ||
| }); | ||
| }; | ||
@@ -220,5 +277,4 @@ | ||
| return P.reduceRight(this, callback); | ||
| } else { | ||
| return P.reduceRight(this, callback, arguments[1]); | ||
| } | ||
| return P.reduceRight(this, callback, arguments[1]); | ||
| }; | ||
@@ -233,8 +289,7 @@ | ||
| }); | ||
| } else { | ||
| var initialValue = P.resolve(arguments[2]); | ||
| return P.resolve(pArray).then(function(arr) { | ||
| return arrayReduceRight.call(arr, reducer(P, callback), initialValue); | ||
| }); | ||
| } | ||
| var initialValue = P.resolve(arguments[2]); | ||
| return P.resolve(pArray).then(function(arr) { | ||
| return arrayReduceRight.call(arr, reducer(P, callback), initialValue); | ||
| }); | ||
| }; | ||
@@ -293,3 +348,6 @@ | ||
| var P = this.constructor || Promise; | ||
| var pArgs = Array.prototype.slice.call(arguments, 1); | ||
| var pArgs = new Array(arguments.length - 1); | ||
| for (var i = 0; i < pArgs.length; ++i) { | ||
| pArgs[i] = arguments[i + 1]; | ||
| } | ||
| return this.then(function(obj) { | ||
@@ -329,3 +387,3 @@ return P.all(pArgs).then(function(args) { | ||
| } | ||
| this['catch'](function(e) { | ||
| this.then0(undefined, function(e) { | ||
| // Throw from new scope to ensure the exception will be unhandled | ||
@@ -377,5 +435,5 @@ // (and thus reported). | ||
| return new P(function(resolve, reject) { | ||
| promise.then(resolve, reject); | ||
| promise.then0(resolve, reject); | ||
| var cleanup = makeRejector(reject, message, ms); | ||
| promise.then(cleanup, cleanup); | ||
| promise.then0(cleanup, cleanup); | ||
| }); | ||
@@ -391,3 +449,6 @@ }; | ||
| // as an iterable. | ||
| var args = Array.prototype.slice.call(arguments); | ||
| var args = new Array(arguments.length); | ||
| for (var i = 0; i < args.length; ++i) { | ||
| args[i] = arguments[i]; | ||
| } | ||
| return P.all(args).then(function(args) { | ||
@@ -410,3 +471,6 @@ var fn = args[0]; | ||
| if (arguments.length <= 1) { return promise['catch'](predicate); } | ||
| var predicates = Array.prototype.slice.call(arguments); | ||
| var predicates = new Array(arguments.length); | ||
| for (var i = 0; i < predicates.length; ++i) { | ||
| predicates[i] = arguments[i]; | ||
| } | ||
| handler = predicates.pop(); | ||
@@ -419,10 +483,10 @@ predicates = predicates.map(function(v) { | ||
| return function(e) { return (e instanceof v); }; | ||
| } else if (typeof v === 'function') { | ||
| } | ||
| if (typeof v === 'function') { | ||
| return function(e) { return !!v(e); }; | ||
| } else { | ||
| return function(e) { | ||
| throw new TypeError('caught filter must inherit from Error ' + | ||
| 'or be a simple predicate function'); | ||
| }; | ||
| } | ||
| return function(e) { | ||
| throw new TypeError('caught filter must inherit from Error ' + | ||
| 'or be a simple predicate function'); | ||
| }; | ||
| }); | ||
@@ -452,8 +516,8 @@ return promise['catch'](function(e) { | ||
| return new P(function(resolve, reject) { | ||
| promise.then(function(value) { | ||
| promise.then0(function(value) { | ||
| var cb = function() { resolve(value); }; | ||
| P.resolve().then(handler).then(cb, reject); | ||
| P.resolve().then(handler).then0(cb, reject); | ||
| }, function(reason) { | ||
| var cb = function() { reject(reason); }; | ||
| P.resolve().then(handler).then(cb, reject); | ||
| P.resolve().then(handler).then0(cb, reject); | ||
| }); | ||
@@ -474,5 +538,17 @@ }); | ||
| var self = this; | ||
| var args = [ fn, this ]; | ||
| Array.prototype.push.apply(args, arguments); | ||
| return P['try'].apply(P, args); | ||
| var args = new Array(arguments.length); | ||
| for (var i = 0; i < args.length; ++i) { | ||
| args[i] = arguments[i]; | ||
| } | ||
| return P.resolve(self).then(function(self) { | ||
| return P.all(args).then(function(args) { | ||
| return new P(function(resolve, reject) { | ||
| try { | ||
| resolve(fn.apply(self, args)); | ||
| } catch (e) { | ||
| reject(e); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
@@ -489,3 +565,3 @@ }; | ||
| }; | ||
| promise.then(function(v) { | ||
| promise.then0(function(v) { | ||
| try { | ||
@@ -511,11 +587,39 @@ callback.call(this, null, v); | ||
| var hasThis = (arguments.length > 2); | ||
| return function() { | ||
| return function(a, b, c) { | ||
| var self = hasThis ? optThis : this; | ||
| var args = Array.prototype.slice.call(arguments); | ||
| var l = arguments.length; | ||
| if (l <= 3 && !names) { | ||
| // This section is a bit redundant, but it improves performance | ||
| // in the common case. | ||
| return new P(function(resolve, reject) { | ||
| var cb = function(e, v) { | ||
| if (e) { | ||
| reject(e); | ||
| } else { | ||
| resolve(v); | ||
| } | ||
| }; | ||
| switch (l) { | ||
| case 0: { nodeFunction.call(self, cb); return; } | ||
| case 1: { nodeFunction.call(self, a, cb); return; } | ||
| case 2: { nodeFunction.call(self, a, b, cb); return; } | ||
| case 3: { nodeFunction.call(self, a, b, c, cb); return; } | ||
| default: { throw new Error('unreachable'); } | ||
| } | ||
| }); | ||
| } | ||
| var args = new Array(l + 1); | ||
| for (var i = 0; i < l; ++i) { | ||
| args[i] = arguments[i]; | ||
| } | ||
| return new P(function(resolve, reject) { | ||
| args.push(function(e, v) { | ||
| args[l] = function(e, v) { | ||
| if (e) { | ||
| reject(e); | ||
| } else if (names === true) { | ||
| resolve(Array.prototype.slice.call(arguments, 1)); | ||
| var a = new Array(arguments.length - 1); | ||
| for (var j = 0; j < a.length; ++j) { | ||
| a[j] = arguments[j + 1]; | ||
| } | ||
| resolve(a); | ||
| } else if (names) { | ||
@@ -530,8 +634,4 @@ var value = {}; | ||
| } | ||
| }); | ||
| try { | ||
| nodeFunction.apply(self, args); | ||
| } catch (e) { | ||
| reject(e); | ||
| } | ||
| }; | ||
| nodeFunction.apply(self, args); | ||
| }); | ||
@@ -565,3 +665,6 @@ }; | ||
| self = this; | ||
| args = arguments; | ||
| args = new Array(arguments.length); | ||
| for (var i = 0; i < args.length; i++) { | ||
| args[i] = arguments[i]; | ||
| } | ||
@@ -638,5 +741,8 @@ return P.resolve(condition()).then(function(exit) { | ||
| })(BoundPromise.prototype.then); | ||
| BoundPromise.prototype.then0 = function(f,r) { this.then(f, r); }; | ||
| // See discussion of PrFunPromise.resolve above: | ||
| BoundPromise.resolve = makeResolve(BoundPromise.resolve); | ||
| if (makeResolve) { | ||
| BoundPromise.resolve = makeResolve(BoundPromise.resolve); | ||
| } | ||
@@ -647,28 +753,44 @@ return newThis ? BoundPromise.resolve(this) : SuperPromise.resolve(this); | ||
| // Generators. | ||
| // Implementation borrowed from Q.async() | ||
| Promise.async = function(makeGenerator) { | ||
| var P = this || Promise; | ||
| return function() { | ||
| var generator, callback, errback; | ||
| // When verb is "send", arg is a value | ||
| // When verb is "throw", arg is an exception | ||
| function continuer(verb, arg) { | ||
| var result; | ||
| var generator = makeGenerator.apply(this, arguments); | ||
| // Isolate try/catch to standalone functions, since v8 | ||
| // will not optimize any method containing a `try` block. | ||
| var errObject = { e: null }; | ||
| var tryCatchNext = function(arg) { | ||
| try { | ||
| result = generator[verb](arg); | ||
| } catch (exception) { | ||
| return P.reject(exception); | ||
| return generator.next(arg); | ||
| } catch (e) { | ||
| errObject.e = e; | ||
| return errObject; | ||
| } | ||
| // XXX: Possibly check for array-like (or iterable) value and | ||
| // use Promise.all() before returning/invoking callback? | ||
| if (result.done) { | ||
| return P.resolve(result.value); | ||
| } else { | ||
| return P.resolve(result.value).then(callback, errback); | ||
| }; | ||
| var tryCatchThrow = function(arg) { | ||
| try { | ||
| return generator['throw'](arg); | ||
| } catch (e) { | ||
| errObject.e = e; | ||
| return errObject; | ||
| } | ||
| } | ||
| generator = makeGenerator.apply(this, arguments); | ||
| callback = continuer.bind(continuer, 'next'); | ||
| errback = continuer.bind(continuer, 'throw'); | ||
| return callback(); | ||
| }; | ||
| return new P(function(resolve, reject) { | ||
| var callback, errback; | ||
| var continuer = function(fn, arg) { | ||
| var result = fn(arg); | ||
| if (result === errObject) { | ||
| reject(result.e); | ||
| return; | ||
| } | ||
| if (result.done) { | ||
| resolve(result.value); | ||
| return; | ||
| } | ||
| // Using then0 here yields a significant performance improvement. | ||
| P.resolve(result.value).then0(callback, errback); | ||
| }; | ||
| callback = function(arg) { return continuer(tryCatchNext, arg); }; | ||
| errback = function(e) { return continuer(tryCatchThrow, e); }; | ||
| callback(); | ||
| }); | ||
| }; | ||
@@ -675,0 +797,0 @@ }; |
+5
-3
| { | ||
| "name": "prfun", | ||
| "version": "2.1.2", | ||
| "version": "2.1.3", | ||
| "description": "Helper functions for ES6 promises", | ||
| "main": "index.js", | ||
| "scripts": { | ||
| "lint": "jshint . && jscs .", | ||
| "jscs": "jscs .", | ||
| "jscs-fix": "jscs --fix .", | ||
| "lint": "jshint . && npm run jscs", | ||
| "lint-no-0.8": "node -e 'process.exit(/v0[.][0-8][.]/.test(process.version) ? 0 : 1)' || npm run lint", | ||
@@ -30,3 +32,3 @@ "mocha": "if node -e 'process.exit(/v0[.]([0-9]|10)[.]/.test(process.version) ? 0 : 1)' ; then mocha ; else mocha --harmony ; fi", | ||
| "core-js": "~1.2.6", | ||
| "jscs": "~2.6.0", | ||
| "jscs": "~2.7.0", | ||
| "jshint": "~2.8.0", | ||
@@ -33,0 +35,0 @@ "mocha": "~2.3.4" |
+18
-0
@@ -102,2 +102,3 @@ # prfun | ||
| - [`Promise#tap`] | ||
| - [`Promise#then0`] | ||
| - [`Promise#throw`] | ||
@@ -738,2 +739,19 @@ - [`Promise.defer`] | ||
| #### `Promise#then0([Function onFulfilled [, Function onRejected]])` → `undefined` | ||
| [`Promise#then0`]: #promisethen0function-onFulfilled--undefined | ||
| This is identical to `Promise#then` except that it does not return | ||
| a value. Some `Promise` implementations (for example, `babybird`) | ||
| export a `then0` implementation which is substantially faster than | ||
| calling `Promise#then`. If such an implementation is present, its | ||
| implementation will be used. Otherwise `prfun` will provide a | ||
| shim implementation that just calls `Promise#then` and discards | ||
| the result. | ||
| This allows you to use `then0` freely in your own code whenever | ||
| you don't care about the result, and `prfun` will use the most | ||
| efficient implementation available. | ||
| <hr> | ||
| #### `Promise#throw(Promise|dynamic reason)` → `Promise` | ||
@@ -740,0 +758,0 @@ [`Promise#throw`]: #promisethrowpromisedynamic-reason--promise |
205401
3.04%4721
2.59%1494
1.22%10
11.11%