Comparing version 3.0.1 to 3.0.2
@@ -7,3 +7,26 @@ 'use strict'; | ||
const Q = require('q'); | ||
const profiler = require('v8-profiler'); | ||
const fs = require('fs'); | ||
let profilerRunning = false; | ||
function toggleProfiling () { | ||
if (profilerRunning) { | ||
const profile = profiler.stopProfiling(); | ||
console.log('stopped profiling'); | ||
profile.export() | ||
.pipe(fs.createWriteStream('./fidelity-' + Date.now() + '.cpuprofile')) | ||
.once('error', profiler.deleteAllProfiles) | ||
.once('finish', profiler.deleteAllProfiles); | ||
profilerRunning = false; | ||
return; | ||
} | ||
profiler.startProfiling(); | ||
profilerRunning = true; | ||
console.log('started profiling'); | ||
} | ||
console.log('PID', process.pid); | ||
process.on('SIGUSR2', toggleProfiling); | ||
function getRandomInt (min, max) { | ||
@@ -23,6 +46,12 @@ return Math.floor(Math.random() * (max - min)) + min; | ||
function fidelityPromise () { | ||
function fidelityResolve () { | ||
return Fidelity.resolve(getRandomInt(0,10)); | ||
} | ||
function fidelityPromise () { | ||
return Fidelity.promise((resolve, reject) => { | ||
resolve(getRandomInt(0,10)); | ||
}); | ||
} | ||
function bluebirdPromise () { | ||
@@ -40,2 +69,8 @@ return Bluebird.resolve(getRandomInt(0,10)); | ||
function PromiseModuleNewPromise () { | ||
return new PromiseModule((resolve, reject) => { | ||
resolve(getRandomInt(0,10)); | ||
}); | ||
} | ||
function runBenchmarks () { | ||
@@ -49,5 +84,8 @@ exports.compare = { | ||
}, | ||
"Fidelity.resolve" : function(done) { | ||
"Fidelity.promise" : function(done) { | ||
fidelityPromise().then(done); | ||
}, | ||
"Fidelity.resolve" : function(done) { | ||
fidelityResolve().then(done); | ||
}, | ||
"Bluebird.resolve" : function (done) { | ||
@@ -61,2 +99,5 @@ bluebirdPromise().then(done); | ||
PromiseModuleResolve().then(done); | ||
}, | ||
"new PromiseModule()" : function (done) { | ||
PromiseModuleNewPromise().then(done); | ||
} | ||
@@ -63,0 +104,0 @@ }; |
122
lib/index.js
'use strict'; | ||
class Handlers { | ||
constructor() { | ||
this.fulfill = null; | ||
this.reject = null; | ||
} | ||
} | ||
/** | ||
* @class promise | ||
*/ | ||
class FidelityPromise { | ||
constructor(state, value) { | ||
this.state = state; | ||
this.value = value; | ||
this.queue = []; | ||
this.handlers = new Handlers(); | ||
this._fidelity = true; | ||
} | ||
/** | ||
* Follows the [Promises/A+](https://promisesaplus.com/) spec | ||
* for a `then` function. | ||
* @returns a promise | ||
* @function then | ||
*/ | ||
then (onFulfilled, onRejected) { | ||
const next = new FidelityPromise(PENDING, null); | ||
if (typeof onFulfilled === 'function') { | ||
next.handlers.fulfill = onFulfilled; | ||
} | ||
if (typeof onRejected === 'function') { | ||
next.handlers.reject = onRejected; | ||
} | ||
this.queue.push(next); | ||
process(this); | ||
return next; | ||
} | ||
catch (onRejected) { | ||
return this.then(null, onRejected); | ||
} | ||
} | ||
const | ||
@@ -7,8 +50,8 @@ PENDING = 0, | ||
REJECTED = 2, | ||
TRUE = factory(FULFILLED, true), | ||
FALSE = factory(FULFILLED, false), | ||
NULL = factory(FULFILLED, null), | ||
UNDEFINED = factory(undefined), | ||
ZERO = factory(FULFILLED, 0), | ||
EMPTYSTRING = factory(FULFILLED, ''), | ||
TRUE = new FidelityPromise(FULFILLED, true), | ||
FALSE = new FidelityPromise(FULFILLED, false), | ||
NULL = new FidelityPromise(FULFILLED, null), | ||
UNDEFINED = new FidelityPromise(undefined), | ||
ZERO = new FidelityPromise(FULFILLED, 0), | ||
EMPTYSTRING = new FidelityPromise(FULFILLED, ''), | ||
@@ -39,12 +82,9 @@ /** | ||
function promise (fn) { | ||
const p = factory(PENDING, null); | ||
const p = new FidelityPromise(PENDING, null); | ||
if (typeof fn === 'function') { | ||
fn( | ||
(v) => { | ||
resolve(p, v); | ||
}, | ||
(r) => { | ||
transition(p, REJECTED, r); | ||
} | ||
); | ||
try { | ||
fn((v) => resolve(p, v), (r) => transition(p, REJECTED, r)); | ||
} catch (e) { | ||
transition(p, REJECTED, e); | ||
} | ||
} | ||
@@ -58,6 +98,7 @@ return p; | ||
* @returns {object} A promise resolved with `value` | ||
* @instance | ||
* @method resolve | ||
* @instance resolve | ||
*/ | ||
function resolveValue (value) { | ||
if (value && value._fidelity) return value; | ||
if (value && value.then) return value; | ||
@@ -70,5 +111,6 @@ switch (value) { | ||
case 0: return ZERO; | ||
case '': return EMPTYSTRING; | ||
} | ||
return factory(FULFILLED, value); | ||
return new FidelityPromise(FULFILLED, value); | ||
} | ||
@@ -107,32 +149,2 @@ | ||
function factory (state, value) { | ||
const p = { | ||
state: state, | ||
value: value, | ||
then: (onFulfilled, onRejected) => { | ||
const q = factory(PENDING, null); | ||
if (typeof onFulfilled === 'function') { | ||
q.handlers.fulfill = onFulfilled; | ||
} | ||
if (typeof onRejected === 'function') { | ||
q.handlers.reject = onRejected; | ||
} | ||
p.queue.push(q); | ||
process(p); | ||
return q; | ||
}, | ||
queue: [], | ||
handlers: { | ||
get: { | ||
fulfill: null, | ||
reject: null | ||
}, | ||
enumerable: false, | ||
configurable: false | ||
}, | ||
_fidelity: true | ||
}; | ||
return p; | ||
} | ||
function resolve (p, x) { | ||
@@ -146,7 +158,3 @@ if (x === p) { | ||
if (x.state === PENDING) { | ||
x.then((value) => { | ||
resolve(p, value); | ||
}, (cause) => { | ||
transition(p, REJECTED, cause); | ||
}); | ||
x.then((value) => resolve(p, value), (cause) => transition(p, REJECTED, cause)); | ||
} else { | ||
@@ -192,5 +200,3 @@ transition(p, x.state, x.value); | ||
if (p.state === FULFILLED) { | ||
handler = qp.handlers.fulfill || ((v) => { | ||
return v; | ||
}); | ||
handler = qp.handlers.fulfill || ((v) => v); | ||
} else if (p.state === REJECTED) { | ||
@@ -210,2 +216,3 @@ handler = qp.handlers.reject || ((r) => { | ||
}); | ||
return p; | ||
} | ||
@@ -219,4 +226,3 @@ | ||
p.value = value; | ||
process(p); | ||
return p; | ||
return process(p); | ||
} |
{ | ||
"name": "fidelity", | ||
"version": "3.0.1", | ||
"version": "3.0.2", | ||
"description": "A simple A+ promises implementation", | ||
@@ -13,3 +13,3 @@ "main": "lib/index.js", | ||
"test": "node test/tape-test.js | tap-spec && promises-aplus-tests test/spec-adapter.js", | ||
"coverage": "istanbul cover tape -- test/tape-test.js | tap-spec", | ||
"coverage": "./coverage.sh", | ||
"docs": "jsdoc --verbose -d docs -t ./node_modules/ink-docstrap/template -R README.md lib/index.js", | ||
@@ -42,3 +42,4 @@ "prepublish": "nsp check", | ||
"tap-spec": "^4.1.1", | ||
"tape": "^4.5.1" | ||
"tape": "^4.5.1", | ||
"v8-profiler": "^5.6.5" | ||
}, | ||
@@ -45,0 +46,0 @@ "repository": { |
214
README.md
@@ -5,2 +5,4 @@ # [![Promises/A+](https://promisesaplus.com/assets/logo-small.png)](https://promisesaplus.com) Fidelity | ||
[![NPM](https://nodei.co/npm/fidelity.png)](https://npmjs.org/package/fidelity) | ||
A simple promises-aplus implementation. | ||
@@ -12,12 +14,24 @@ | ||
## API Documentation | ||
## Usage | ||
The API is pretty simple, and it's lightly documented [here](http://lanceball.com/fidelity/). | ||
A fidelity promise behaves according to the | ||
[Promises/A+ specification](https://promisesaplus.com/). If you haven't read it, | ||
it's worth your time and will probably make all of the fidelity documentation clearer. | ||
## Usage | ||
You can create promises using the `promise` function. | ||
A fidelity `promise` takes a function as an argument. This function accepts a | ||
`resolve` and a `reject` object. Suppose we have a function `f()` that takes | ||
some time to complete asynchronously. We can call this function using a promise. | ||
var Fidelity = require('fidelity'); | ||
Fidelity.promise( (resolve, reject) => { | ||
// etc. | ||
} ) | ||
You call the `promise` function with a function as the only parameter. Typically this | ||
function will perform some asynchronous task, and when that task has completed it will | ||
resolve or reject the promise depending on whether or not the task completed successfully. | ||
The function takes two function parameters: `resolve` and `reject`. These functions are | ||
used to resolve or reject the promise as needed. Suppose we have a function, | ||
`someAsyncFunction()` that takes some time to complete asynchronously. We can call | ||
this function using a promise. | ||
var Fidelity = require('fidelity'); | ||
@@ -28,15 +42,45 @@ | ||
if (err) { | ||
reject(err); | ||
reject(err); // The function produced an error. Reject the promise | ||
} else { | ||
resolve(result); | ||
resolve(result); // Fulfill the promise with the result | ||
} | ||
}); | ||
}).then( (val) => { | ||
}) | ||
.then( (val) => { | ||
// This code executes after a promise has been fulfilled | ||
// Do something with the result. | ||
}) | ||
.catch( (err) => { | ||
// This code executes if the promise was rejected | ||
}); | ||
### Promise states | ||
A promise will only ever be in one of three states. `PENDING`, `FULFILLED` or `REJECTED`. | ||
## API | ||
### Fidelity | ||
The `fidelity` module exports an object from which the API is derived | ||
const Fidelity = require('fidelity'); | ||
// { | ||
// promise: [Function: promise], | ||
// deferred: [Function: deferred], | ||
// resolve: [Function: resolve] | ||
// }; | ||
### Fidelity.promise(func) | ||
A factory function that creates and returns a promise. The `func` parameter is a function | ||
that accepts a `resolve` and `reject` function. | ||
### Fidelity.promise(f).then(onFulfilled, onRejected) | ||
The promise object returned from `promise()` has a function, `then()`. This | ||
takes two function arguments. The first is called with the return | ||
takes two function arguments. The first, `onFulfilled`, is called with the return | ||
value (if any) of the promise function if it is successfully fulfilled. The | ||
second function is called in the event of an error. | ||
second function, `onRejected` is called in the event of an error. A `promise` | ||
is returned in either case. | ||
@@ -49,2 +93,46 @@ p.then( (result) => { | ||
### Fidelity.promise(f).catch(onRejected) | ||
This is just a little syntactic sugar for `promise.then(null, onRejected);`. | ||
It returns a `promise`. | ||
### Fidelity.resolve(value) | ||
Returns a promise that has been resolved with the provided `value`. | ||
### Fidelity.deferred() | ||
Creates and returns a `deferred` object, containing a promise which may | ||
be resolved or rejected at some point in the future. | ||
An example. | ||
const deferred = Fidelity.deferred(); | ||
callSomeAsyncFunction((err, result) => { | ||
if (err) { | ||
deferred.reject(err); | ||
} else { | ||
deferred.resolve(result); | ||
} | ||
}); | ||
### Fidelity.deferred().resolve(value) | ||
Resolves the deferred promise with `value`. | ||
### Fidelity.deferred().reject(cause) | ||
Rejects the deferred promise with `cause`. | ||
### Fidelity.deferred().promise | ||
The deferred promise. | ||
## Testing | ||
This module passes all of the tests in the | ||
[Promises/A+ Compliance Test Suite](https://github.com/promises-aplus/promises-tests). | ||
To run the full suite of the Promises/A+ spec, just `npm test` from the command line. | ||
## Benchmarks | ||
@@ -55,3 +143,3 @@ | ||
so take this with a grain of salt. Results from a simplified, non-scientific benchmark | ||
performed on a Macbook Pro on a random Thursday morning. | ||
performed on a Macbook Pro on a random Tuesday afternoon. Your results may vary. | ||
@@ -66,4 +154,4 @@ ~/s/fidelity git:master ❮❮❮ npm run benchmark ⏎ ⬆ ✭ ✱ | ||
{ http_parser: '2.7.0', | ||
node: '6.2.0', | ||
v8: '5.0.71.47', | ||
node: '6.4.0', | ||
v8: '5.0.71.60', | ||
uv: '1.9.1', | ||
@@ -79,64 +167,74 @@ zlib: '1.2.8', | ||
Raw: | ||
> 1452.0469530469531 | ||
> 1451.051948051948 | ||
> 1328.9690309690309 | ||
> 1381.3556443556442 | ||
Average (mean) 1403.355894105894 | ||
> 1555.3626373626373 | ||
> 1401.2167832167831 | ||
> 1327.6563436563436 | ||
> 1393.0969030969031 | ||
Average (mean) 1419.3331668331666 | ||
new PromiseModule() | ||
Raw: | ||
> 1365.4745254745255 | ||
> 1343.7552447552448 | ||
> 1191.027972027972 | ||
> 1181.5374625374625 | ||
Average (mean) 1270.4488011988012 | ||
Fidelity.resolve | ||
Raw: | ||
> 480.5814185814186 | ||
> 470.0689310689311 | ||
> 442.5074925074925 | ||
> 518.3576423576424 | ||
Average (mean) 477.87887112887114 | ||
> 933.9120879120879 | ||
> 896.8631368631369 | ||
> 870.8951048951049 | ||
> 922.7932067932068 | ||
Average (mean) 906.1158841158842 | ||
Bluebird.resolve | ||
Fidelity.promise | ||
Raw: | ||
> 412.5564435564436 | ||
> 427.02897102897106 | ||
> 401.5834165834166 | ||
> 417.4425574425574 | ||
Average (mean) 414.6528471528472 | ||
> 785.4055944055945 | ||
> 777.1188811188811 | ||
> 712.4645354645355 | ||
> 734.8341658341658 | ||
Average (mean) 752.4557942057943 | ||
native Promise.resolve | ||
Raw: | ||
> 426.4095904095904 | ||
> 387.86313686313684 | ||
> 385.55944055944053 | ||
> 440.2147852147852 | ||
Average (mean) 410.01173826173823 | ||
> 420.1108891108891 | ||
> 426.6373626373626 | ||
> 403.24175824175825 | ||
> 405.8771228771229 | ||
Average (mean) 413.96678321678326 | ||
Bluebird.resolve | ||
Raw: | ||
> 441.4175824175824 | ||
> 401.4165834165834 | ||
> 399.82917082917083 | ||
> 410.04495504495503 | ||
Average (mean) 413.1770729270729 | ||
new Promise() | ||
Raw: | ||
> 390.84115884115886 | ||
> 373.2167832167832 | ||
> 387.4725274725275 | ||
> 376.1778221778222 | ||
Average (mean) 381.9270729270729 | ||
> 396.83116883116884 | ||
> 374.0979020979021 | ||
> 368.3986013986014 | ||
> 397.9230769230769 | ||
Average (mean) 384.3126873126873 | ||
Q() | ||
Raw: | ||
> 142.8181818181818 | ||
> 139.21678321678323 | ||
> 138.32867132867133 | ||
> 142.3116883116883 | ||
Average (mean) 140.66883116883116 | ||
> 145.3106893106893 | ||
> 141.88645418326692 | ||
> 138.93106893106892 | ||
> 137.1878121878122 | ||
Average (mean) 140.82900615320932 | ||
Winner: PromiseModule.resolve | ||
Compared with next highest (Fidelity.resolve), it's: | ||
65.95% faster | ||
2.94 times as fast | ||
0.47 order(s) of magnitude faster | ||
QUITE A BIT FASTER | ||
Compared with next highest (new PromiseModule()), it's: | ||
10.49% faster | ||
1.12 times as fast | ||
0.05 order(s) of magnitude faster | ||
A LITTLE FASTER | ||
Compared with the slowest (Q()), it's: | ||
89.98% faster | ||
9.98 times as fast | ||
90.08% faster | ||
10.08 times as fast | ||
1 order(s) of magnitude faster | ||
## Testing | ||
This module passes all of the tests in the | ||
[Promises/A+ Compliance Test Suite](https://github.com/promises-aplus/promises-tests). | ||
To run the full suite of the Promises/A+ spec, just `npm test` from the command line. |
@@ -94,3 +94,3 @@ 'use strict'; | ||
const p = Fidelity.promise((resolve, reject) => { | ||
resolve('First resolved value'); | ||
process.nextTick(() => resolve('First resolved value')); | ||
}); | ||
@@ -107,2 +107,45 @@ | ||
}); | ||
test('Fidelity.promise.catch()', (t) => { | ||
const p = Fidelity.promise((resolve, reject) => { | ||
throw new Error('Test exception'); | ||
}) | ||
.then((_) => { | ||
t.fail('Promise should short circuit to catch'); | ||
}) | ||
.catch((e) => { | ||
t.strictEqual(e.message, 'Test exception'); | ||
t.end(); | ||
}); | ||
}); | ||
test('promise.then.catch()', (t) => { | ||
const p = Fidelity.promise((resolve, reject) => { | ||
resolve('Test value'); | ||
}) | ||
.then((v) => { | ||
t.strictEqual(v, 'Test value'); | ||
throw new Error('Test exception'); | ||
}) | ||
.catch((e) => { | ||
t.strictEqual(e.message, 'Test exception'); | ||
t.end(); | ||
}); | ||
}); | ||
test('Fidelity.resolve', (t) => { | ||
t.strictEqual(Fidelity.resolve(null).value, null); | ||
t.strictEqual(Fidelity.resolve(undefined).value, undefined); | ||
t.strictEqual(Fidelity.resolve(true).value, true); | ||
t.strictEqual(Fidelity.resolve(false).value, false); | ||
t.strictEqual(Fidelity.resolve(0).value, 0); | ||
t.strictEqual(Fidelity.resolve('').value, ''); | ||
t.strictEqual(Fidelity.resolve(Infinity).value, Infinity); | ||
t.strictEqual(Fidelity.resolve(-Infinity).value, -Infinity); | ||
t.strictEqual(Fidelity.resolve(Fidelity.promise((r) => { | ||
r('Test resolution'); | ||
})).value, 'Test resolution'); | ||
t.end(); | ||
}); | ||
}); |
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
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
22501
13
437
234
13
2