Comparing version 0.9.4 to 0.9.5
For pull requests: | ||
- Be consistent with prevelant style and design decisions. | ||
- Be consistent with prevalent style and design decisions. | ||
- Add a Jasmine spec to `specs/q-spec.js`. | ||
@@ -9,3 +9,3 @@ - Use `npm test` to avoid regressions. | ||
can find the will to deal with. | ||
- You do not need to build minified versions. | ||
- Do not build minified versions; we do this each release. | ||
- If you would be so kind, add a note to `CHANGES.md` in an | ||
@@ -25,13 +25,5 @@ appropriate section: | ||
- Run `npm run cover` and make sure you're happy with the results. | ||
- Arrange for the Google Closure Compiler to be available as a | ||
`closure` command then run `source minify` to build `q.min.js` and | ||
`q.min.js.gz` and commit with `Minify`. | ||
```bash | ||
#!/bin/bash | ||
java -jar `which closure.jar` $@ | ||
``` | ||
- Note the size of `q.min.js.gz` in `README.md` if it has changed to 1 | ||
significant digit. | ||
- Run `npm run minify` and be sure to commit the resulting `q.min.js`. | ||
- Note the Gzipped size output by the previous command, and update | ||
`README.md` if it has changed to 1 significant digit. | ||
- Stash any local changes. | ||
@@ -41,4 +33,4 @@ - Update `CHANGES.md` to reflect all changes in the differences | ||
credit is due. | ||
- Update `README.md` to address all new, non-expiermental features. | ||
- Update the API reference on the Wiki to reflect all non-expiermental | ||
- Update `README.md` to address all new, non-experimental features. | ||
- Update the API reference on the Wiki to reflect all non-experimental | ||
features. | ||
@@ -45,0 +37,0 @@ - Use `npm version major|minor|patch` to update `package.json`, |
{ | ||
"name": "q", | ||
"version": "0.9.4", | ||
"version": "0.9.5", | ||
"description": "A library for promises (CommonJS/Promises/A,B,D)", | ||
@@ -47,7 +47,10 @@ "homepage": "https://github.com/kriskowal/q", | ||
"devDependencies": { | ||
"jshint": ">=1.1.0", | ||
"jshint": "~2.1.3", | ||
"cover": "*", | ||
"jasmine-node": "1.2.2", | ||
"opener": "*", | ||
"promises-aplus-tests": "1.x" | ||
"promises-aplus-tests": "1.x", | ||
"grunt": "~0.4.1", | ||
"grunt-cli": "~0.1.9", | ||
"grunt-contrib-uglify": "~0.2.2" | ||
}, | ||
@@ -58,3 +61,5 @@ "scripts": { | ||
"lint": "jshint q.js", | ||
"cover": "cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html" | ||
"cover": "cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html", | ||
"minify": "grunt", | ||
"prepublish": "grunt" | ||
}, | ||
@@ -61,0 +66,0 @@ "overlay": { |
222
q.js
@@ -31,3 +31,3 @@ // vim:ts=4:sts=4:sw=4: | ||
// Turn off strict mode for this function so we can assign to global.Q | ||
/*jshint strict: false, -W117*/ | ||
/* jshint strict: false */ | ||
@@ -298,2 +298,3 @@ // This file will function properly as a <script> tag, or a module | ||
try { | ||
/* jshint evil: true, nonew: false */ | ||
new Function("(function* (){ yield 1; })"); | ||
@@ -307,4 +308,2 @@ hasES6Generators = true; | ||
Q.longStackJumpLimit = 1; | ||
var STACK_JUMP_SEPARATOR = "From previous event:"; | ||
@@ -322,5 +321,12 @@ | ||
) { | ||
error.stack = filterStackString(error.stack) + | ||
"\n" + STACK_JUMP_SEPARATOR + "\n" + | ||
filterStackString(promise.stack); | ||
var stacks = []; | ||
for (var p = promise; !!p; p = p.source) { | ||
if (p.stack) { | ||
stacks.unshift(p.stack); | ||
} | ||
} | ||
stacks.unshift(error.stack); | ||
var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n"); | ||
error.stack = filterStackString(concatedStacks); | ||
} | ||
@@ -405,2 +411,13 @@ } | ||
function deprecate(callback, name, alternative) { | ||
return function () { | ||
if (typeof console !== "undefined" && | ||
typeof console.warn === "function") { | ||
console.warn(name + " is deprecated, use " + alternative + | ||
" instead.", new Error("").stack); | ||
} | ||
return callback.apply(callback, arguments); | ||
}; | ||
} | ||
// end of shims | ||
@@ -410,5 +427,5 @@ // beginning of real work | ||
/** | ||
* Creates fulfilled promises from non-promises, | ||
* Creates fulfilled promises from non-thenables, | ||
* Passes Q promises through, | ||
* Coerces thenables to Q promises. | ||
* Coerces other thenables to Q promises. | ||
*/ | ||
@@ -426,9 +443,15 @@ function Q(value) { | ||
/** | ||
* Constructs a {promise, resolve} object. | ||
* Controls whether or not long stack traces will be on | ||
*/ | ||
Q.longStackSupport = false; | ||
/** | ||
* Constructs a {promise, resolve, reject} object. | ||
* | ||
* The resolver is a callback to invoke with a more resolved value for the | ||
* promise. To fulfill the promise, invoke the resolver with any value that is | ||
* not a function. To reject the promise, invoke the resolver with a rejection | ||
* object. To put the promise in the same state as another promise, invoke the | ||
* resolver with that other promise. | ||
* `resolve` is a callback to invoke with a more resolved value for the | ||
* promise. To fulfill the promise, invoke `resolve` with any value that is | ||
* not a thenable. To reject the promise, invoke `resolve` with a rejected | ||
* thenable, or invoke `reject` with the reason directly. To resolve the | ||
* promise to another thenable, thus putting it in the same state, invoke | ||
* `resolve` with that other thenable. | ||
*/ | ||
@@ -442,3 +465,3 @@ Q.defer = defer; | ||
// promise using the `resolve` function because it handles both fully | ||
// resolved values and other promises gracefully. | ||
// non-thenable values and other thenables gracefully. | ||
var messages = [], progressListeners = [], resolvedPromise; | ||
@@ -463,14 +486,22 @@ | ||
promise.valueOf = function () { | ||
// XXX deprecated | ||
promise.valueOf = deprecate(function () { | ||
if (messages) { | ||
return promise; | ||
} | ||
var nearer = valueOf(resolvedPromise); | ||
if (isPromise(nearer)) { | ||
resolvedPromise = nearer; // shorten chain | ||
var nearerValue = nearer(resolvedPromise); | ||
if (isPromise(nearerValue)) { | ||
resolvedPromise = nearerValue; // shorten chain | ||
} | ||
return nearer; | ||
return nearerValue; | ||
}, "valueOf", "inspect"); | ||
promise.inspect = function () { | ||
if (!resolvedPromise) { | ||
return { state: "pending" }; | ||
} | ||
return resolvedPromise.inspect(); | ||
}; | ||
if (Q.longStackJumpLimit > 0 && hasStacks) { | ||
if (Q.longStackSupport && hasStacks) { | ||
try { | ||
@@ -493,8 +524,9 @@ throw new Error(); | ||
function become(promise) { | ||
resolvedPromise = promise; | ||
function become(newPromise) { | ||
resolvedPromise = newPromise; | ||
promise.source = newPromise; | ||
array_reduce(messages, function (undefined, message) { | ||
nextTick(function () { | ||
promise.promiseDispatch.apply(promise, message); | ||
newPromise.promiseDispatch.apply(newPromise, message); | ||
}); | ||
@@ -597,3 +629,3 @@ }, void 0); | ||
Q.makePromise = makePromise; | ||
function makePromise(descriptor, fallback, valueOf, exception, isException) { | ||
function makePromise(descriptor, fallback, inspect) { | ||
if (fallback === void 0) { | ||
@@ -625,8 +657,19 @@ fallback = function (op) { | ||
if (valueOf) { | ||
promise.valueOf = valueOf; | ||
} | ||
promise.inspect = inspect; | ||
if (isException) { | ||
promise.exception = exception; | ||
// XXX deprecated `valueOf` and `exception` support | ||
if (inspect) { | ||
var inspected = inspect(); | ||
if (inspected.state === "rejected") { | ||
promise.exception = inspected.reason; | ||
} | ||
promise.valueOf = deprecate(function () { | ||
var inspected = inspect(); | ||
if (inspected.state === "pending" || | ||
inspected.state === "rejected") { | ||
return promise; | ||
} | ||
return inspected.value; | ||
}); | ||
} | ||
@@ -657,3 +700,3 @@ | ||
"get", "set", "del", "delete", | ||
"post", "send", "invoke", | ||
"post", "send", "mapply", "invoke", "mcall", | ||
"keys", | ||
@@ -665,3 +708,3 @@ "fapply", "fcall", "fbind", | ||
"nfcall", "nfapply", "nfbind", "denodeify", "nbind", | ||
"npost", "nsend", "ninvoke", | ||
"npost", "nsend", "nmapply", "ninvoke", "nmcall", | ||
"nodeify" | ||
@@ -697,6 +740,11 @@ ], | ||
*/ | ||
Q.nearer = valueOf; | ||
function valueOf(value) { | ||
// XXX should we re-do this? | ||
Q.nearer = nearer; | ||
function nearer(value) { | ||
if (isPromise(value)) { | ||
return value.valueOf(); | ||
var inspected = value.inspect(); | ||
if (inspected.state === "fulfilled") { | ||
return inspected.value; | ||
} | ||
} | ||
@@ -726,3 +774,3 @@ return value; | ||
function isPending(object) { | ||
return !isFulfilled(object) && !isRejected(object); | ||
return isPromise(object) && object.inspect().state === "pending"; | ||
} | ||
@@ -736,3 +784,3 @@ | ||
function isFulfilled(object) { | ||
return !isPromiseAlike(valueOf(object)); | ||
return !isPromise(object) || object.inspect().state === "fulfilled"; | ||
} | ||
@@ -745,4 +793,3 @@ | ||
function isRejected(object) { | ||
object = valueOf(object); | ||
return isPromise(object) && "exception" in object; | ||
return isPromise(object) && object.inspect().state === "rejected"; | ||
} | ||
@@ -812,3 +859,3 @@ | ||
function untrackRejection(promise, reason) { | ||
function untrackRejection(promise) { | ||
if (!trackUnhandledRejections) { | ||
@@ -860,5 +907,5 @@ return; | ||
return this; | ||
}, function valueOf() { | ||
return this; | ||
}, reason, true); | ||
}, function inspect() { | ||
return { state: "rejected", reason: reason }; | ||
}); | ||
@@ -876,15 +923,15 @@ // Note that the reason has not been handled. | ||
Q.fulfill = fulfill; | ||
function fulfill(object) { | ||
function fulfill(value) { | ||
return makePromise({ | ||
"when": function () { | ||
return object; | ||
return value; | ||
}, | ||
"get": function (name) { | ||
return object[name]; | ||
return value[name]; | ||
}, | ||
"set": function (name, value) { | ||
object[name] = value; | ||
"set": function (name, rhs) { | ||
value[name] = rhs; | ||
}, | ||
"delete": function (name) { | ||
delete object[name]; | ||
delete value[name]; | ||
}, | ||
@@ -895,15 +942,15 @@ "post": function (name, args) { | ||
if (name === null || name === void 0) { | ||
return object.apply(void 0, args); | ||
return value.apply(void 0, args); | ||
} else { | ||
return object[name].apply(object, args); | ||
return value[name].apply(value, args); | ||
} | ||
}, | ||
"apply": function (thisP, args) { | ||
return object.apply(thisP, args); | ||
return value.apply(thisP, args); | ||
}, | ||
"keys": function () { | ||
return object_keys(object); | ||
return object_keys(value); | ||
} | ||
}, void 0, function valueOf() { | ||
return object; | ||
}, void 0, function inspect() { | ||
return { state: "fulfilled", value: value }; | ||
}); | ||
@@ -925,12 +972,4 @@ } | ||
} | ||
// In order to break infinite recursion or loops between `then` and | ||
// `resolve`, it is necessary to attempt to extract fulfilled values | ||
// out of foreign promise implementations before attempting to wrap | ||
// them as unresolved promises. It is my hope that other | ||
// implementations will implement `valueOf` to synchronously extract | ||
// the fulfillment value from their fulfilled promises. If the | ||
// other promise library does not implement `valueOf`, the | ||
// implementations on primordial prototypes are harmless. | ||
value = valueOf(value); | ||
// assimilate thenables, CommonJS/Promises/A+ | ||
// assimilate thenables | ||
if (isPromiseAlike(value)) { | ||
@@ -976,3 +1015,3 @@ return coerce(value); | ||
}, function () { | ||
return valueOf(object); | ||
return resolve(object).inspect(); | ||
}); | ||
@@ -1153,2 +1192,14 @@ } | ||
/** | ||
* The spawn function is a small wrapper around async that immediately | ||
* calls the generator and also ends the promise chain, so that any | ||
* unhandled errors are thrown instead of forwarded to the error | ||
* handler. This is useful because it's extremely common to run | ||
* generators at the top-level to work with libraries. | ||
*/ | ||
Q.spawn = spawn; | ||
function spawn(makeGenerator) { | ||
Q.done(Q.async(makeGenerator)()); | ||
} | ||
// FIXME: Remove this interface once ES6 generators are in SpiderMonkey. | ||
@@ -1186,3 +1237,3 @@ /** | ||
* The promised function decorator ensures that any promise arguments | ||
* are resolved and passed as values (`this` is also resolved and passed | ||
* are settled and passed as values (`this` is also settled and passed | ||
* as a value). It will also ensure that the result of a function is | ||
@@ -1279,2 +1330,3 @@ * always a promise. | ||
var post = Q.post = dispatcher("post"); | ||
Q.mapply = post; // experimental | ||
@@ -1290,2 +1342,3 @@ /** | ||
Q.invoke = send; // synonyms | ||
Q.mcall = send; // experimental | ||
function send(value, name) { | ||
@@ -1337,3 +1390,3 @@ var args = array_slice(arguments, 2); | ||
* @param object promise or immediate reference for target object | ||
* @return promise for the keys of the eventually resolved object | ||
* @return promise for the keys of the eventually settled object | ||
*/ | ||
@@ -1357,4 +1410,6 @@ Q.keys = dispatcher("keys"); | ||
array_reduce(promises, function (undefined, promise, index) { | ||
if (isFulfilled(promise)) { | ||
promises[index] = valueOf(promise); | ||
var snapshot; | ||
if (isPromise(promise) && | ||
(snapshot = promise.inspect()).state === "fulfilled") { | ||
promises[index] = snapshot.value; | ||
} else { | ||
@@ -1378,3 +1433,3 @@ ++countDown; | ||
/** | ||
* Waits for all promises to be resolved, either fulfilled or | ||
* Waits for all promises to be settled, either fulfilled or | ||
* rejected. This is distinct from `all` since that would stop | ||
@@ -1387,3 +1442,3 @@ * waiting at the first rejection. The promise returned by | ||
*/ | ||
Q.allResolved = allResolved; | ||
Q.allResolved = deprecate(allResolved, "allResolved", "allSettled"); | ||
function allResolved(promises) { | ||
@@ -1400,2 +1455,21 @@ return when(promises, function (promises) { | ||
Q.allSettled = allSettled; | ||
function allSettled(values) { | ||
return when(values, function (values) { | ||
return all(array_map(values, function (value, i) { | ||
return when( | ||
value, | ||
function (fulfillmentValue) { | ||
values[i] = { state: "fulfilled", value: fulfillmentValue }; | ||
return values[i]; | ||
}, | ||
function (reason) { | ||
values[i] = { state: "rejected", reason: reason }; | ||
return values[i]; | ||
} | ||
); | ||
})).thenResolve(values); | ||
}); | ||
} | ||
/** | ||
@@ -1430,3 +1504,3 @@ * Captures the failure of a promise, giving an oportunity to recover | ||
/** | ||
* Provides an opportunity to observe the rejection of a promise, | ||
* Provides an opportunity to observe the settling of a promise, | ||
* regardless of whether the promise is fulfilled or rejected. Forwards | ||
@@ -1628,2 +1702,3 @@ * the resolution to the returned promise when the callback is done. | ||
Q.npost = npost; | ||
Q.nmapply = npost; // synonyms | ||
function npost(object, name, args) { | ||
@@ -1650,2 +1725,3 @@ var nodeArgs = array_slice(args || []); | ||
Q.ninvoke = Q.nsend; // synonyms | ||
Q.nmcall = Q.nsend; // synonyms | ||
function nsend(object, name /*, ...args*/) { | ||
@@ -1652,0 +1728,0 @@ var nodeArgs = array_slice(arguments, 2); |
114
README.md
@@ -69,3 +69,3 @@ [![Build Status](https://secure.travis-ci.org/kriskowal/q.png?branch=master)](http://travis-ci.org/kriskowal/q) | ||
- A ``<script>`` tag (creating a ``Q`` global variable): ~3 KB minified and | ||
- A ``<script>`` tag (creating a ``Q`` global variable): ~2.5 KB minified and | ||
gzipped. | ||
@@ -288,3 +288,3 @@ - A Node.js and CommonJS module, available in [npm](https://npmjs.org/) as | ||
The ``all`` function returns a promise for an array of values. When this | ||
The ``all`` function returns a promise for an array of values. When this | ||
promise is fulfilled, the array contains the fulfillment values of the original | ||
@@ -294,14 +294,14 @@ promises, in the same order as those promises. If one of the given promises | ||
rest of the batch. If you want to wait for all of the promises to either be | ||
fulfilled or rejected, you can use ``allResolved``. | ||
fulfilled or rejected, you can use ``allSettled``. | ||
```javascript | ||
Q.allResolved(promises) | ||
.then(function (promises) { | ||
promises.forEach(function (promise) { | ||
if (promise.isFulfilled()) { | ||
var value = promise.valueOf(); | ||
Q.allSettled(promises) | ||
.then(function (results) { | ||
results.forEach(function (result) { | ||
if (result.state === "fulfilled") { | ||
var value = result.value; | ||
} else { | ||
var exception = promise.valueOf().exception; | ||
var reason = result.reason; | ||
} | ||
}) | ||
}); | ||
}); | ||
@@ -689,44 +689,44 @@ ``` | ||
There is a ``makeNodeResolver`` method on deferreds that is handy for | ||
the NodeJS callback pattern. | ||
If you're working with functions that make use of the Node.js callback pattern, | ||
Q provides a few useful utility functions for converting between them. The | ||
most straightforward are probably `Q.nfcall` and `Q.nfapply` ("Node function | ||
call/apply") for calling Node.js-style functions and getting back a promise: | ||
```javascript | ||
var deferred = Q.defer(); | ||
FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver()); | ||
return deferred.promise; | ||
return Q.nfcall(FS.readFile, "foo.txt", "utf-8"); | ||
return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]); | ||
``` | ||
And there are ``Q.nfcall`` and ``Q.ninvoke`` for even shorter | ||
expression. | ||
If you are working with methods, instead of simple functions, you can easily | ||
run in to the usual problems where passing a method to another function—like | ||
`Q.nfcall`—"un-binds" the method from its owner. To avoid this, you can either | ||
use `Function.prototype.bind` or some nice shortcut methods we provide: | ||
```javascript | ||
return Q.nfcall(FS.readFile, "foo.txt", "utf-8"); | ||
return Q.ninvoke(redisClient, "get", "user:1:id"); | ||
return Q.npost(redisClient, "get", ["user:1:id"]); | ||
``` | ||
You can also create reusable wrappers with `Q.denodeify` or `Q.nbind`: | ||
```javascript | ||
return Q.ninvoke(FS, "readFile", "foo.txt", "utf-8"); | ||
var readFile = Q.denodeify(FS.readFile); | ||
return readFile("foo.txt", "utf-8"); | ||
var redisClientGet = Q.nbind(redisClient.get, redisClient); | ||
return redisClientGet("user:1:id"); | ||
``` | ||
There is also a ``Q.nfbind`` function that that creates a reusable | ||
wrapper. | ||
Finally, if you're working with raw deferred objects, there is a | ||
`makeNodeResolver` method on deferreds that can be handy: | ||
```javascript | ||
var readFile = Q.nfbind(FS.readFile); | ||
return readFile("foo.txt", "utf-8"); | ||
var deferred = Q.defer(); | ||
FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver()); | ||
return deferred.promise; | ||
``` | ||
Note that, since promises are always resolved in the next turn of the | ||
event loop, working with streams [can be tricky][streams]. The | ||
essential problem is that, since Node does not buffer input, it is | ||
necessary to attach your ``"data"`` event listeners immediately, | ||
before this next turn comes around. There are a variety of solutions | ||
to this problem, and even some hope that in future versions of Node it | ||
will [be ameliorated][streamsnext]. | ||
[streams]: https://groups.google.com/d/topic/q-continuum/xr8znxc_K5E/discussion | ||
[streamsnext]: http://maxogden.com/node-streams#streams.next | ||
### Long Stack Traces | ||
Q comes with *experimental* support for “long stack traces,” wherein the `stack` | ||
Q comes with optional support for “long stack traces,” wherein the `stack` | ||
property of `Error` rejection reasons is rewritten to be traced along | ||
@@ -745,3 +745,3 @@ asynchronous jumps instead of stopping at the most recent one. As an example: | ||
gives a strack trace of: | ||
usually would give a rather unhelpful stack trace looking something like | ||
@@ -751,25 +751,16 @@ ``` | ||
at explode (/path/to/test.js:3:11) | ||
From previous event: | ||
at theDepthsOfMyProgram (/path/to/test.js:2:16) | ||
at Object.<anonymous> (/path/to/test.js:7:1) | ||
at _fulfilled (/path/to/test.js:q:54) | ||
at resolvedValue.promiseDispatch.done (/path/to/q.js:823:30) | ||
at makePromise.promise.promiseDispatch (/path/to/q.js:496:13) | ||
at pending (/path/to/q.js:397:39) | ||
at process.startup.processNextTick.process._tickCallback (node.js:244:9) | ||
``` | ||
Note how you can see the the function that triggered the async operation in the | ||
stack trace! This is very helpful for debugging, as otherwise you end up getting | ||
only the first line, plus a bunch of Q internals, with no sign of where the | ||
operation started. | ||
But, if you turn this feature on by setting | ||
This feature comes with some caveats, however. First, it does not (<em>yet!</em>) | ||
stitch together multiple asynchronous steps. You only get the one immediately | ||
prior to the operation that throws. Secondly, it comes with a performance | ||
penalty, and so if you are using Q to create many promises in a | ||
performance-critical situation, you will probably want to turn it off. | ||
To turn it off, set | ||
```js | ||
Q.longStackJumpLimit = 0; | ||
Q.longStackSupport = true; | ||
``` | ||
Then you stack traces will revert to their usual unhelpful selves: | ||
then the above code gives a nice stack trace to the tune of | ||
@@ -779,9 +770,16 @@ ``` | ||
at explode (/path/to/test.js:3:11) | ||
at _fulfilled (/path/to/test.js:q:54) | ||
at resolvedValue.promiseDispatch.done (/path/to/q.js:823:30) | ||
at makePromise.promise.promiseDispatch (/path/to/q.js:496:13) | ||
at pending (/path/to/q.js:397:39) | ||
at process.startup.processNextTick.process._tickCallback (node.js:244:9) | ||
From previous event: | ||
at theDepthsOfMyProgram (/path/to/test.js:2:16) | ||
at Object.<anonymous> (/path/to/test.js:7:1) | ||
``` | ||
Note how you can see the the function that triggered the async operation in the | ||
stack trace! This is very helpful for debugging, as otherwise you end up getting | ||
only the first line, plus a bunch of Q internals, with no sign of where the | ||
operation started. | ||
This feature does come with somewhat-serious performance and memory overhead, | ||
however. If you're working with lots of promises, or trying to scale a server | ||
to many users, you should probably keep it off. But in development, go for it! | ||
## Reference | ||
@@ -788,0 +786,0 @@ |
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
80783
8
6
1573
803