promise-tests
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -58,2 +58,5 @@ "use strict"; | ||
describe("State transitions", function () { | ||
// NOTE: Promises/A does not specify that attempts to change state twice | ||
// should be silently ignored, so we allow implementations to throw | ||
// exceptions. See resolution-races.js for more info. | ||
it("cannot fulfill twice", function (done) { | ||
@@ -67,3 +70,5 @@ var tuple = pending(); | ||
tuple.fulfill(sentinel); | ||
tuple.fulfill(other); | ||
try { | ||
tuple.fulfill(other); | ||
} catch (e) { } | ||
}); | ||
@@ -79,3 +84,5 @@ | ||
tuple.reject(sentinel); | ||
tuple.reject(other); | ||
try { | ||
tuple.reject(other); | ||
} catch (e) { } | ||
}); | ||
@@ -91,3 +98,5 @@ | ||
tuple.fulfill(sentinel); | ||
tuple.reject(other); | ||
try { | ||
tuple.reject(other); | ||
} catch (e) { } | ||
}); | ||
@@ -103,3 +112,5 @@ | ||
tuple.reject(sentinel); | ||
tuple.fulfill(other); | ||
try { | ||
tuple.fulfill(other); | ||
} catch (e) { } | ||
}); | ||
@@ -106,0 +117,0 @@ }); |
{ | ||
"name": "promise-tests", | ||
"description": "A test suite for CommonJS Promises/A", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"author": "Domenic Denicola <domenic@domenicdenicola.com> (http://domenicdenicola.com)", | ||
@@ -20,3 +20,3 @@ "license": "WTFPL", | ||
"test": "mocha lib/promises-a.js --reporter spec", | ||
"test-extensions": "mocha lib/common-extensions.js --reporter spec", | ||
"test-extensions": "mocha lib/always-async.js lib/resolution-races.js lib/returning-a-promise.js --reporter spec", | ||
"lint": "jshint lib" | ||
@@ -23,0 +23,0 @@ }, |
@@ -29,4 +29,10 @@ # A Promises/A Test Suite | ||
## An Important Spec Extension | ||
## Other Included Tests | ||
Promises/A is a rather bare spec. Most promise implementations have converged on certain semantics which make working | ||
with promises much more pleasant. Those tests are included in other files in the `lib` directory, and can be run with | ||
`npm run test-extensions`, although you will need to go manually edit the adapter lines as before. | ||
### Returning a Promise from a Handler | ||
There is, unfortunately, a very common and important behavior of thenables that is actually *not* in the Promises/A | ||
@@ -49,6 +55,49 @@ spec: what happens when one of your handlers returns a promise? For concreteness, let's use this example: | ||
Tests for this are included in `lib/common-extensions.js` and can be run with `npm run test-extensions`, using the | ||
same adapter framework as above. Currently jQuery, Q, and when.js pass, while promise-stream fails. | ||
Tests for this spec extension are included as `lib/returning-a-promise.js`. | ||
### Resolution Races | ||
As described in the "Requirements" section of the [CommonJS wiki on Promises][wiki], number 3.2, you should be able to | ||
distribute the resolver to multiple mutually-suspicious consumers, and have them "race" to resolve the promise. This is | ||
somewhat analogous to the synchronous case where there can be a "race" between multiple `return` and `throw` statements | ||
within the same function. It's useful for implementing cases like a race between a timeout rejection and a normal | ||
resolution, as in Q's [`Q.timeout(promise, ms)`][timeout]. And it has some security implications in the | ||
[object-capability][] sense. | ||
In particular, this means that resolvers (i.e. someone with only the ability to fulfill or reject a promise) should not | ||
be able to observe the state of the promise so far. For example, attempting to resolve multiple times should not throw | ||
an error, since that would be a way for someone with only resolver capabilities to determine a promise's state. However, | ||
the Promises/A spec itself failed to capture this requirement, even though the CommonJS group considered it important, | ||
so implementations are still Promises/A conforming if they throw errors. | ||
Tests for this spec extension are included as `lib/resolution-races.js`. | ||
[object-capability]: http://en.wikipedia.org/wiki/Object-capability_model | ||
[wiki]: http://wiki.commonjs.org/wiki/Promises | ||
[timeout]: https://github.com/kriskowal/q/blob/c2c7353dfa5341b1f57bd5f4c3ac40064bf3e63f/q.js#L1445-1465 | ||
### Always Async | ||
It's generally more predictable if you're guaranteed that your resolution callbacks are always called in a future turn | ||
of the event loop. This allows you to know the execution order of code like the following with confidence: | ||
```js | ||
console.log("1"); | ||
promise.then(function () { | ||
console.log("3"); | ||
}); | ||
console.log("2"); | ||
``` | ||
If a promise library does not guarantee asynchronicity, then in some cases the sequence will be 1, 2, 3, while in others | ||
it will be 1, 3, 2. This makes code hard to follow as your assumptions about what is true inside the callback do not | ||
always hold. Leading promise libraries are sure to always call resolution callbacks in the next turn of the event loop, | ||
using mechanisms like `process.nextTick` in Node or `setTimeout(..., 0)` in browsers. | ||
Tests for this spec extension are included as `lib/always-async.js` | ||
## Room for Improvement | ||
@@ -55,0 +104,0 @@ |
30254
12
574
117