Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: kriskowal/q
...
head fork: kriskowal/q
  • 11 commits
  • 10 files changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 29, 2012
Kris Kowal Removed harmless hint of things maybe to come 27b0931
Kris Kowal Revisited boilerplate strategy
Using this strategy saves 13 bytes from the minified and gzipped output
because the argument name can be reduced to a single character but the
array and string cannot.
dd491af
Kris Kowal Nits
 - Update copyright range.
 - Update JSHint declarations.
 - Expand abbreviated variable.
 - Add inline docs.
86cf184
Kris Kowal Fix long-standing bug in promiseSend protocol
I do not know whether this mistake ever manifested itself, but
promiseSend should never return a value, but previous attempts to to
remove the return broke several tests and I never bothered to isolate
the cause.  It turns out to be that "when" depended on the return value
in one case, in which it should not have.
ea8c5a1
Kris Kowal Add fapply, fcall, fbind
These will replace apply, call, bind in the next major version.
They do not accept a thisp.
6fb67e6
Commits on Apr 30, 2012
Kris Kowal Note that view and apply will be removed 29e6a61
Kris Kowal Add npost and ninvoke d6ec023
Kris Kowal Switched from using send to dispatch internally
Dispatch involves less variadic argument torture.
ec1edcd
Kris Kowal Fixed missing conversion from reason to error
All reasons are now exceptions.
6be15b9
Kris Kowal Remove support for new in bind
Also normalize the use of thisp as a variable name.
71df2ef
Kris Kowal Revise shim strategy, approaching makeQ
Preliminary motions toward satisfying SES requirements, may also help
minification.
7742589
10 CHANGES.md
View
@@ -8,6 +8,16 @@
Use ``nbind``.
- WARNING: The deprecated ``deferred.node()`` interface will be
removed. Use ``deferred.makeNodeResolver()``.
+ - WARNING: The deprecated ``call``, ``apply``, and ``bind`` are
+ replaced with ``fcall``, ``fapply``, and ``fbind``. Use of a
+ ``thisp`` is discouraged. For calling methods, use ``post`` or
+ ``invoke``.
+ - WARNING: The undocumented ``view`` and ``viewInfo`` will be removed.
+
+## Next minor
+
+ - Added ``fapply``, ``fcall``, ``fbind`` for non-thisp promised
+ function calls.
## 0.8.4
33 README.md
View
@@ -29,7 +29,7 @@ step1(function (value1) {
With a promise library, you can flatten the pyramid.
```javascript
-Q.call(step1)
+Q.fcall(step1)
.then(step2)
.then(step3)
.then(step4)
@@ -406,7 +406,7 @@ You can create a promise from a value using ``Q.call``. This returns a
promise for 10.
```javascript
-return Q.call(function () {
+return Q.fcall(function () {
return 10;
});
```
@@ -414,7 +414,7 @@ return Q.call(function () {
You can also use ``call`` to get a promise for an exception.
```javascript
-return Q.call(function () {
+return Q.fcall(function () {
throw new Error("Can't do it");
})
```
@@ -425,7 +425,7 @@ numbers. The second argument is the ``this`` object to pass into the
function.
```javascript
-return Q.call(eventualAdd, null, 2, 2);
+return Q.fcall(eventualAdd, null, 2, 2);
```
When nothing else will do the job, you can use ``defer``, which is
@@ -448,7 +448,7 @@ Note that a deferred can be resolved with a value or a promise. The
promise.
```javascript
-var rejection = Q.call(function () {
+var rejection = Q.fcall(function () {
throw new Error("Can't do it");
});
deferred.resolve(rejection);
@@ -502,7 +502,7 @@ return Q.all([a, b]);
```
```javascript
-return Q.call(function () {
+return Q.fcall(function () {
return [a, b];
})
.all();
@@ -522,10 +522,10 @@ return Q.when($.ajax(...))
If there is any chance that the promise you receive is not a Q promise
as provided by your library, you should wrap it using a Q function.
-You can even use ``Q.call`` as a shorthand.
+You can even use ``Q.invoke`` as a shorthand.
```javascript
-return Q.call($.ajax, $, ...)
+return Q.invoke($, 'ajax', ...)
.then(function () {
})
```
@@ -546,10 +546,8 @@ value.foo = value promise.put("foo", value)
delete value.foo promise.del("foo")
value.foo(...args) promise.post("foo", [args])
value.foo(...args) promise.invoke("foo", ...args)
-value(...args) promise.apply(null, [args])
-value(...args) promise.call(null, ...args)
-value.call(thisp, ...args) promise.apply(thisp, [args])
-value.apply(thisp, [args]) promise.call(thisp, ...args)
+value(...args) promise.fapply([args])
+value(...args) promise.fcall(...args)
```
If the promise is a proxy for a remote object, you can shave
@@ -563,7 +561,7 @@ shorthand for particularly-simple value handlers. For example, you
can replace
```javascript
-return Q.call(function () {
+return Q.fcall(function () {
return [{ foo: "bar" }, { foo: "baz" }];
})
.then(function (value) {
@@ -574,7 +572,7 @@ return Q.call(function () {
with
```javascript
-return Q.call(function () {
+return Q.fcall(function () {
return [{ foo: "bar" }, { foo: "baz" }];
})
.get(0)
@@ -593,12 +591,17 @@ FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver());
return deferred.promise;
```
-And there’s a ``Q.ncall`` function for shorter.
+And there are ``Q.ncall`` and ``Q.ninvoke`` for even shorter
+expression.
```javascript
return Q.ncall(FS.readFile, FS, "foo.txt", "utf-8");
```
+```javascript
+return Q.ninvoke(FS, 'readFile', "foo.txt", "utf-8");
+```
+
There is also a ``Q.nbind`` function that that creates a reusable
wrapper.
445 q.js
View
@@ -1,17 +1,34 @@
// vim:ts=4:sts=4:sw=4:
/*jshint browser: true, node: true,
- curly: true, eqeqeq: true, noarg: true, nonew: true, trailing: true, undef: true
+ curly: true, eqeqeq: true, noarg: true, nonew: true, trailing: true,
+ undef: true
*/
-/*global define: false, Q: true */
+/*global define: false, Q: true, msSetImmediate: true, setImmediate: true,
+ MessageChannel: true */
/*!
*
+ * Copyright 2009-2012 Kris Kowal under the terms of the MIT
+ * license found at http://github.com/kriskowal/q/raw/master/LICENSE
+ *
+ * With parts by Tyler Close
* Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
* at http://www.opensource.org/licenses/mit-license.html
* Forked at ref_send.js version: 2009-05-11
*
- * Copyright 2009-2011 Kris Kowal under the terms of the MIT
- * license found at http://github.com/kriskowal/q/raw/master/LICENSE
+ * With parts by Mark Miller
+ * Copyright (C) 2011 Google Inc.
*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
(function (definition) {
@@ -23,18 +40,48 @@
// RequireJS
if (typeof define === "function") {
- define(["exports"], definition);
+ define(definition);
+
// CommonJS
} else if (typeof exports === "object") {
- definition(exports);
+ definition(void 0, exports);
+
+ // SES (Secure EcmaScript)
+ } else if (typeof ses !== "undefined") {
+ if (!ses.ok()) {
+ return;
+ } else {
+ ses.makeQ = function () {
+ var Q = {};
+ return definition(void 0, Q);
+ };
+ }
+
// <script>
} else {
- definition(Q = {});
+ definition(void 0, Q = {});
}
-})(function (exports) {
+})(function (require, exports) {
"use strict";
+// shims
+
+// used for default "defend" and in "allResolved"
+var noop = function () {};
+
+// for the security conscious, defend may be a deep freeze as provided
+// by cajaVM. Otherwise we try to provide a shallow freeze just to
+// discourage promise changes that are not compatible with secure
+// usage. If Object.freeze does not exist, fall back to doing nothing
+// (no op).
+var defend = Object.freeze || noop;
+if (typeof cajaVM !== "undefined") {
+ defend = cajaVM.def;
+}
+
+// use the fastest possible means to execute a task in a future turn
+// of the event loop.
var nextTick;
if (typeof process !== "undefined") {
// node
@@ -54,7 +101,7 @@ if (typeof process !== "undefined") {
channel.port1.onmessage = function () {
head = head.next;
var task = head.task;
- head.task = null;
+ delete head.task;
task();
};
nextTick = function (task) {
@@ -68,74 +115,78 @@ if (typeof process !== "undefined") {
};
}
-// useful for an identity stub and default resolvers
-function identity(x) { return x; }
-
-// shims
-function shim(object, name, shimmed) {
- if (!object[name]) {
- object[name] = shimmed;
- }
- return object[name];
+// Attempt to make generics safe in the face of downstream
+// modifications.
+// There is no situation where this is necessary.
+// If you need a security guarantee, these primordials need to be
+// deeply frozen anyway, and if you don’t need a security guarantee,
+// this is just plain paranoid.
+// However, this does have the nice side-effect of reducing the size
+// of the code by reducing x.call() to merely x(), eliminating many
+// hard-to-minify characters.
+// See Mark Miller’s explanation of what this does.
+// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
+var uncurryThis;
+// I have kept both variations because the first is theoretically
+// faster, if bind is available.
+if (Function.prototype.bind) {
+ var Function_bind = Function.prototype.bind;
+ uncurryThis = Function_bind.bind(Function_bind.call);
+} else {
+ uncurryThis = function (f) {
+ return function (thisp) {
+ return f.call.apply(f, arguments);
+ };
+ };
}
-var freeze = shim(Object, "freeze", identity);
+var Array_slice = uncurryThis(Array.prototype.slice);
-var create = shim(Object, "create", function (prototype) {
- function Type() { }
- Type.prototype = prototype;
- return new Type();
-});
-
-var keys = shim(Object, "keys", function (object) {
- var keys = [];
- for (var key in object) {
- keys.push(key);
- }
- return keys;
-});
-
-var reduce = Array.prototype.reduce || function (callback, basis) {
- var i = 0,
- ii = this.length;
+var Array_reduce = uncurryThis(Array.prototype.reduce || function (callback, basis) {
+ var index = 0,
+ length = this.length;
// concerning the initial value, if one is not provided
if (arguments.length === 1) {
// seek to the first value in the array, accounting
// for the possibility that is is a sparse array
do {
- if (i in this) {
- basis = this[i++];
+ if (index in this) {
+ basis = this[index++];
break;
}
- if (++i >= ii) {
+ if (++index >= length) {
throw new TypeError();
}
} while (1);
}
// reduce
- for (; i < ii; i++) {
+ for (; index < length; index++) {
// account for the possibility that the array is sparse
- if (i in this) {
- basis = callback(basis, this[i], i);
+ if (index in this) {
+ basis = callback(basis, this[index], index);
}
}
return basis;
-};
+});
-function isStopIteration(exception) {
- return Object.prototype.toString.call(exception) ===
- "[object StopIteration]";
-}
+var Object_create = Object.create || function (prototype) {
+ function Type() { }
+ Type.prototype = prototype;
+ return new Type();
+};
-// Abbreviations for performance and minification
-var slice = Array.prototype.slice;
-function valueOf(value) {
- // if !Object.isObject(value)
- if (Object(value) !== value) {
- return value;
- } else {
- return value.valueOf();
+var Object_keys = Object.keys || function (object) {
+ var keys = [];
+ for (var key in object) {
+ keys.push(key);
}
+ return keys;
+};
+
+var Object_toString = Object.prototype.toString;
+
+function isStopIteration(exception) {
+ return Object_toString(exception) === "[object StopIteration]";
}
/**
@@ -154,7 +205,6 @@ exports.nextTick = nextTick;
* resolver with that other promise.
*/
exports.defer = defer;
-
function defer() {
// if "pending" is an "Array", that indicates that the promise has not yet
// been resolved. If it is "undefined", it has been resolved. Each
@@ -164,11 +214,11 @@ function defer() {
// resolved values and other promises gracefully.
var pending = [], value;
- var deferred = create(defer.prototype);
- var promise = create(makePromise.prototype);
+ var deferred = Object_create(defer.prototype);
+ var promise = Object_create(makePromise.prototype);
promise.promiseSend = function () {
- var args = slice.call(arguments);
+ var args = Array_slice(arguments);
if (pending) {
pending.push(args);
} else {
@@ -190,7 +240,7 @@ function defer() {
return;
}
value = resolve(resolvedValue);
- reduce.call(pending, function (undefined, pending) {
+ Array_reduce(pending, function (undefined, pending) {
nextTick(function () {
value.promiseSend.apply(value, pending);
});
@@ -199,7 +249,9 @@ function defer() {
return value;
}
- deferred.promise = freeze(promise);
+ defend(promise);
+
+ deferred.promise = promise;
deferred.resolve = become;
deferred.reject = function (exception) {
return become(reject(exception));
@@ -208,20 +260,30 @@ function defer() {
return deferred;
}
-defer.prototype.node = // XXX deprecated
+/**
+ * Creates a Node-style callback that will resolve or reject the deferred
+ * promise.
+ * @returns a nodeback
+ */
defer.prototype.makeNodeResolver = function () {
var self = this;
return function (error, value) {
if (error) {
self.reject(error);
} else if (arguments.length > 2) {
- self.resolve(Array.prototype.slice.call(arguments, 1));
+ self.resolve(Array_slice(arguments, 1));
} else {
self.resolve(value);
}
};
};
+/**
+ * @param makePromise {Function} a function that returns nothing and accepts
+ * the resolve and reject functions for a deferred.
+ * @returns a promise that may be resolved with the given resolve and reject
+ * functions, or rejected by a thrown exception in makePromise
+ */
exports.promise = promise;
function promise(makePromise) {
var deferred = defer();
@@ -229,8 +291,7 @@ function promise(makePromise) {
makePromise,
void 0,
deferred.resolve,
- deferred.reject,
- deferred.progress
+ deferred.reject
).fail(deferred.reject);
return deferred.promise;
}
@@ -250,14 +311,14 @@ exports.makePromise = makePromise;
function makePromise(descriptor, fallback, valueOf, rejected) {
if (fallback === void 0) {
fallback = function (op) {
- return reject("Promise does not support operation: " + op);
+ return reject(new Error("Promise does not support operation: " + op));
};
}
- var promise = create(makePromise.prototype);
+ var promise = Object_create(makePromise.prototype);
promise.promiseSend = function (op, resolved /* ...args */) {
- var args = slice.call(arguments, 2);
+ var args = Array_slice(arguments, 2);
var result;
try {
if (descriptor[op]) {
@@ -268,7 +329,7 @@ function makePromise(descriptor, fallback, valueOf, rejected) {
} catch (exception) {
result = reject(exception);
}
- return (resolved || identity)(result);
+ resolved(result);
};
if (valueOf) {
@@ -279,7 +340,9 @@ function makePromise(descriptor, fallback, valueOf, rejected) {
promise.promiseRejected = true;
}
- return freeze(promise);
+ defend(promise);
+
+ return promise;
}
// provide thenables, CommonJS/Promises/A
@@ -288,7 +351,7 @@ makePromise.prototype.then = function (fulfilled, rejected) {
};
// Chainable methods
-reduce.call(
+Array_reduce(
[
"isResolved", "isFulfilled", "isRejected",
"when", "spread", "send",
@@ -296,6 +359,7 @@ reduce.call(
"post", "invoke",
"keys",
"apply", "call", "bind",
+ "fapply", "fcall", "fbind",
"all", "allResolved",
"view", "viewInfo",
"timeout", "delay",
@@ -305,7 +369,7 @@ reduce.call(
makePromise.prototype[name] = function () {
return exports[name].apply(
exports,
- [this].concat(slice.call(arguments))
+ [this].concat(Array_slice(arguments))
);
};
},
@@ -320,7 +384,26 @@ makePromise.prototype.toString = function () {
return '[object Promise]';
};
-freeze(makePromise.prototype);
+defend(makePromise.prototype);
+
+/**
+ * If an object is not a promise, it is as "near" as possible.
+ * If a promise is rejected, it is as "near" as possible too.
+ * If it’s a fulfilled promise, the fulfillment value is nearer.
+ * If it’s a deferred promise and the deferred has been resolved, the
+ * resolution is "nearer".
+ * @param object
+ * @returns most resolved (nearest) form of the object
+ */
+exports.nearer = valueOf;
+function valueOf(value) {
+ // if !Object.isObject(value)
+ if (Object(value) !== value) {
+ return value;
+ } else {
+ return value.valueOf();
+ }
+}
/**
* @returns whether the given object is a promise.
@@ -454,6 +537,9 @@ function resolve(object) {
"properties": properties
};
},
+ "fapply": function (args) {
+ return object.apply(void 0, args);
+ },
"keys": function () {
return keys(object);
}
@@ -476,7 +562,7 @@ function master(object) {
return makePromise({
"isDef": function () {}
}, function fallback(op) {
- var args = slice.call(arguments);
+ var args = Array_slice(arguments);
return send.apply(void 0, [object].concat(args));
}, function () {
return valueOf(object);
@@ -492,7 +578,7 @@ function viewInfo(object, info) {
return info;
}
}, function fallback(op) {
- var args = slice.call(arguments);
+ var args = Array_slice(arguments);
return send.apply(void 0, [object].concat(args));
}, function () {
return valueOf(object);
@@ -514,7 +600,7 @@ function view(object) {
view = {};
}
var properties = info.properties || {};
- Object.keys(properties).forEach(function (name) {
+ Object_keys(properties).forEach(function (name) {
if (properties[name] === "function") {
view[name] = function () {
return post(object, name, arguments);
@@ -568,10 +654,11 @@ function when(value, fulfilled, rejected) {
return;
}
done = true;
- deferred.resolve(
- resolve(value)
- .promiseSend("when", _fulfilled, _rejected)
- );
+ resolve(value).promiseSend("when", function (value) {
+ deferred.resolve(_fulfilled(value));
+ }, function (exception) {
+ deferred.resolve(_rejected(exception));
+ });
}, function (exception) {
if (done) {
return;
@@ -652,14 +739,12 @@ function async(makeGenerator) {
/**
* Constructs a promise method that can be used to safely observe resolution of
* a promise for an arbitrarily named method like "propfind" in a future turn.
- *
- * "sender" constructs methods like "get(promise, name)" and "put(promise)".
*/
exports.sender = sender;
exports.Method = sender; // XXX deprecated
function sender(op) {
return function (object) {
- var args = slice.call(arguments, 1);
+ var args = Array_slice(arguments, 1);
return send.apply(void 0, [object, op].concat(args));
};
}
@@ -674,7 +759,27 @@ function sender(op) {
exports.send = send;
function send(object, op) {
var deferred = defer();
- var args = slice.call(arguments, 2);
+ var args = Array_slice(arguments, 2);
+ object = resolve(object);
+ nextTick(function () {
+ object.promiseSend.apply(
+ object,
+ [op, deferred.resolve].concat(args)
+ );
+ });
+ return deferred.promise;
+}
+
+/**
+ * sends a message to a value in a future turn
+ * @param object* the recipient
+ * @param op the name of the message operation, e.g., "when",
+ * @param args further arguments to be forwarded to the operation
+ * @returns result {Promise} a promise for the result of the operation
+ */
+exports.dispatch = dispatch;
+function dispatch(object, op, args) {
+ var deferred = defer();
object = resolve(object);
nextTick(function () {
object.promiseSend.apply(
@@ -686,12 +791,25 @@ function send(object, op) {
}
/**
+ * Constructs a promise method that can be used to safely observe resolution of
+ * a promise for an arbitrarily named method like "propfind" in a future turn.
+ *
+ * "dispatcher" constructs methods like "get(promise, name)" and "put(promise)".
+ */
+exports.dispatcher = dispatcher;
+function dispatcher(op) {
+ return function (object) {
+ var args = Array_slice(arguments, 1);
+ return dispatch(object, op, args);
+ };
+}
+/**
* Gets the value of a property in a future turn.
* @param object promise or immediate reference for target object
* @param name name of property to get
* @return promise for the property value
*/
-exports.get = sender("get");
+exports.get = dispatcher("get");
/**
* Sets the value of a property in a future turn.
@@ -700,7 +818,7 @@ exports.get = sender("get");
* @param value new value of property
* @return promise for the return value
*/
-exports.put = sender("put");
+exports.put = dispatcher("put");
/**
* Deletes a property in a future turn.
@@ -708,7 +826,7 @@ exports.put = sender("put");
* @param name name of property to delete
* @return promise for the return value
*/
-exports.del = exports['delete'] = sender("del");
+exports.del = exports['delete'] = dispatcher("del");
/**
* Invokes a method in a future turn.
@@ -722,7 +840,7 @@ exports.del = exports['delete'] = sender("del");
* JSON serializable object.
* @return promise for the return value
*/
-var post = exports.post = sender("post");
+var post = exports.post = dispatcher("post");
/**
* Invokes a method in a future turn.
@@ -732,61 +850,76 @@ var post = exports.post = sender("post");
* @return promise for the return value
*/
exports.invoke = function (value, name) {
- var args = slice.call(arguments, 2);
+ var args = Array_slice(arguments, 2);
return post(value, name, args);
};
/**
* Applies the promised function in a future turn.
* @param object promise or immediate reference for target function
- * @param context the context object (this) for the call
+ * @param thisp the `this` object for the call
* @param args array of application arguments
*/
-var apply = exports.apply = sender("apply");
+var apply = exports.apply = dispatcher("apply");
+
+/**
+ * Applies the promised function in a future turn.
+ * @param object promise or immediate reference for target function
+ * @param args array of application arguments
+ */
+var fapply = exports.fapply = dispatcher("fapply");
/**
* Calls the promised function in a future turn.
* @param object promise or immediate reference for target function
- * @param context the context object (this) for the call
+ * @param thisp the `this` object for the call
* @param ...args array of application arguments
*/
-exports.call = exports['try'] = call;
-function call(value, context) {
- var args = slice.call(arguments, 2);
- return apply(value, context, args);
+exports.call = call;
+function call(value, thisp) {
+ var args = Array_slice(arguments, 2);
+ return apply(value, thisp, args);
+}
+
+/**
+ * Calls the promised function in a future turn.
+ * @param object promise or immediate reference for target function
+ * @param ...args array of application arguments
+ */
+exports.fcall = exports['try'] = fcall;
+function fcall(value) {
+ var args = Array_slice(arguments, 1);
+ return fapply(value, args);
}
/**
* Binds the promised function, transforming return values into a fulfilled
* promise and thrown errors into a rejected one.
* @param object promise or immediate reference for target function
- * @param context the context object (this) for the call
+ * @param thisp the `this` object for the call
* @param ...args array of application arguments
*/
exports.bind = bind;
-function bind(value, context) {
- var args = slice.call(arguments, 2);
-
+function bind(value, thisp) {
+ var args = Array_slice(arguments, 2);
return function bound() {
- var allArgs = args.concat(slice.call(arguments));
-
- if (this instanceof bound) {
- var F = function () { };
- F.prototype = value.prototype;
- var self = new F();
-
- var result = apply(value, self, allArgs);
+ var allArgs = args.concat(Array_slice(arguments));
+ return apply(value, thisp, allArgs);
+ };
+}
- return result.then(function (fulfilledValue) {
- // if Object.isObject(fulfilledValue)
- if (Object(fulfilledValue) === fulfilledValue) {
- return fulfilledValue;
- }
- return self;
- });
- } else {
- return apply(value, context, allArgs);
- }
+/**
+ * Binds the promised function, transforming return values into a fulfilled
+ * promise and thrown errors into a rejected one.
+ * @param object promise or immediate reference for target function
+ * @param ...args array of application arguments
+ */
+exports.fbind = fbind;
+function fbind(value) {
+ var args = Array_slice(arguments, 1);
+ return function fbound() {
+ var allArgs = args.concat(Array_slice(arguments));
+ return fapply(value, allArgs);
};
}
@@ -796,7 +929,7 @@ function bind(value, context) {
* @param object promise or immediate reference for target object
* @return promise for the keys of the eventually resolved object
*/
-exports.keys = sender("keys");
+exports.keys = dispatcher("keys");
/**
* Turns an array of promises into a promise for an array. If any of
@@ -815,7 +948,7 @@ function all(promises) {
return resolve(promises);
}
var deferred = defer();
- reduce.call(promises, function (undefined, promise, index) {
+ Array_reduce(promises, function (undefined, promise, index) {
when(promise, function (value) {
promises[index] = value;
if (--countDown === 0) {
@@ -832,7 +965,7 @@ exports.allResolved = allResolved;
function allResolved(promises) {
return when(promises, function (promises) {
return when(all(promises.map(function (promise) {
- return when(promise, identity, identity);
+ return when(promise, noop, noop);
})), function () {
return promises.map(resolve);
});
@@ -936,34 +1069,6 @@ function delay(promise, timeout) {
}
/**
- * Wraps a NodeJS continuation passing function and returns an equivalent
- * version that returns a promise.
- *
- * Q.nbind(FS.readFile, FS)(__filename)
- * .then(console.log)
- * .end()
- *
- */
-exports.nbind = nbind;
-exports.node = nbind; // XXX deprecated
-function nbind(callback /* thisp, ...args*/) {
- if (arguments.length > 1) {
- var args = Array.prototype.slice.call(arguments, 1);
- callback = callback.bind.apply(callback, args);
- }
- return function () {
- var deferred = defer();
- var args = slice.call(arguments);
- // add a continuation that resolves the promise
- args.push(deferred.node());
- // trap exceptions thrown by the callback
- apply(callback, this, args)
- .fail(deferred.reject);
- return deferred.promise;
- };
-}
-
-/**
* Passes a continuation to a Node function, which is called with a given
* `this` value and arguments provided as an array, and returns a promise.
*
@@ -990,8 +1095,48 @@ function napply(callback, thisp, args) {
*/
exports.ncall = ncall;
function ncall(callback, thisp /*, ...args*/) {
- var args = slice.call(arguments, 2);
+ var args = Array_slice(arguments, 2);
return napply(callback, thisp, args);
}
+/**
+ * Wraps a NodeJS continuation passing function and returns an equivalent
+ * version that returns a promise.
+ *
+ * Q.nbind(FS.readFile, FS)(__filename)
+ * .then(console.log)
+ * .end()
+ *
+ */
+exports.nbind = nbind;
+function nbind(callback /* thisp, ...args*/) {
+ if (arguments.length > 1) {
+ var args = Array_slice(arguments, 1);
+ callback = callback.bind.apply(callback, args);
+ }
+ return function () {
+ var deferred = defer();
+ var args = Array_slice(arguments);
+ // add a continuation that resolves the promise
+ args.push(deferred.makeNodeResolver());
+ // trap exceptions thrown by the callback
+ apply(callback, this, args)
+ .fail(deferred.reject);
+ return deferred.promise;
+ };
+}
+
+exports.npost = npost;
+function npost(object, name, args) {
+ return napply(object[name], name, args);
+}
+
+exports.ninvoke = ninvoke;
+function ninvoke(object, name /*, ...args*/) {
+ var args = Array_slice(arguments, 2);
+ return napply(object[name], name, args);
+}
+
+defend(exports);
+
});
84 test/bind.js
View
@@ -112,90 +112,6 @@ exports['test uses existing context if none given'] = function (ASSERT, done) {
.fin(done);
};
-exports['test invoking with new'] = function (ASSERT, done) {
- function Point(x, y) {
- this.x = x;
- this.y = y;
- }
-
- var BoundPoint = Q.bind(Point, null, 1);
-
- (new BoundPoint(2))
- .then(function (point) {
- ASSERT.deepEqual(point, { x: 1, y: 2 }, "fulfilled with constructed");
- })
- .fail(function (reason) {
- ASSERT.ok(false, reason);
- }).
- fin(done);
-};
-
-exports['test returns correctly when new\'ed'] = function (ASSERT, done) {
- var returned = {};
-
- var bound = Q.bind(function () { return returned; });
-
- (new bound())
- .then(function (val) {
- ASSERT.strictEqual(val, returned, "fulfilled with correct value");
- })
- .fail(function (reason) {
- ASSERT.ok(false, reason);
- })
- .fin(done);
-};
-
-exports['test throwing when new\'ed'] = function (ASSERT, done) {
- var throwMe = new Error("boo!");
-
- var bound = Q.bind(function () {
- throw throwMe;
- });
-
- (new bound())
- .then(function () {
- ASSERT.ok(false, "should not be fulfilled");
- })
- .fail(function (reason) {
- ASSERT.strictEqual(reason, throwMe, "rejected with correct reason");
- })
- .fin(done);
-};
-
-exports['test returns only objects when new\'ed'] = function (ASSERT, done) {
- var bound = Q.bind(function () {
- this.x = 1;
-
- return 5;
- });
-
- (new bound())
- .then(function (val) {
- ASSERT.deepEqual(val, { x: 1 }, "fulfilled with object not primitive");
- })
- .fail(function (reason) {
- ASSERT.ok(false, reason);
- })
- .fin(done);
-};
-
-exports['test returns only objects when new\'ed 2'] = function (ASSERT, done) {
- var bound = Q.bind(function () {
- this.x = 1;
-
- return Q.resolve(5);
- });
-
- (new bound())
- .then(function (val) {
- ASSERT.deepEqual(val, { x: 1 }, "fulfilled with object not primitive");
- })
- .fail(function (reason) {
- ASSERT.ok(false, reason);
- })
- .fin(done);
-};
-
if (module == require.main) {
require('test').run(exports);
}
26 test/map.js
View
@@ -0,0 +1,26 @@
+
+var Q = require("../q");
+
+Q.resolve([1, 2, 3])
+.map(function (n) {
+ console.log('A', n);
+ return Q.delay(n + 1, 100 * Math.random());
+})
+.map(function (n) {
+ console.log('B', n);
+ return n + 1;
+})
+.map(function (n) {
+ console.log('C', n);
+ return n + 1;
+})
+.map(function (n) {
+ console.log('D', n);
+ return n + 1;
+})
+.all()
+.then(function (all) {
+ console.log('final', all);
+})
+.end()
+
83 test/progress.js
View
@@ -0,0 +1,83 @@
+var Q = require("../q");
+
+function foo(ticks) {
+ return Q.promise(function (resolve, reject, progress) {
+ var at = 0;
+ function tick() {
+ if (at >= ticks) {
+ resolve();
+ } else {
+ setTimeout(tick, 100);
+ }
+ progress(at++ / ticks);
+ }
+ tick();
+ });
+}
+
+function analyze(promise) {
+ var deferred = Q.defer();
+ Q.when(promise, deferred.resolve, deferred.reject, function (progress, time) {
+ var duration = time / 1000;
+ var rate = progress / duration;
+ var estimate = (1 - progress) * rate * 1000;
+ deferred.progress(progress, estimate);
+ })
+ return deferred.promise;
+}
+
+/*
+foo(10)
+.to(analyze)
+.progress(function (progress, estimate) {
+ console.log("reticulating splines:", (progress * 100).toFixed(0) + "% in " + (estimate / 1000).toFixed(2) + " seconds");
+})
+.then(function (value) {
+ console.log("splines reticulated");
+})
+.end()
+*/
+
+/*
+Q.promise(function (resolve, reject, progress) {
+ Q.begin()
+ .then(function (value, time) {
+ return foo(5)
+ .progress(function(ratio, time) {
+ progress(ratio * 5/10);
+ })
+ })
+ .then(function (value, time) {
+ return foo(2)
+ .progress(function(ratio, time) {
+ progress(5/10 + ratio * 2/10);
+ })
+ })
+ .then(function (value, time) {
+ return foo(3)
+ .progress(function(ratio, time) {
+ progress(7/10 + 3/10 * ratio);
+ })
+ })
+ .then(resolve, reject);
+})
+.progress(function (ratio, time) {
+ console.log("reticulating splines:", ratio, time.toFixed());
+})
+.then(function (value, time) {
+ console.log("splines reticulated:", time.toFixed());
+})
+.end()
+*/
+
+Q.begin()
+.then(function () {
+ return foo(5)
+ .progress(function () {
+ })
+})
+.progress(function (ratio, time) {
+ console.log(ratio, time);
+})
+.end();
+
4 test/spread.js
View
@@ -3,7 +3,7 @@
var Q = require("../q");
exports['test spread'] = function (ASSERT, done) {
- Q.ref([1,2,3])
+ Q.resolve([1,2,3])
.spread(function (a, b, c) {
ASSERT.equal(a, 1, 'spread 1');
ASSERT.equal(b, 2, 'spread 2');
@@ -17,7 +17,7 @@ exports['test spread'] = function (ASSERT, done) {
};
exports['test spread all'] = function (ASSERT, done) {
- Q.ref([1,2,3].map(Q.ref))
+ Q.resolve([1,2,3].map(Q.resolve))
.all()
.spread(function (a, b, c) {
ASSERT.equal(a, 1, 'spread 1');
159 test/stream.js
View
@@ -0,0 +1,159 @@
+var Q = require("../q");
+
+var renewalInterval = 1e10; // a long time
+
+function makeQueue() {
+ var ends = Q.defer();
+ return {
+ "send": function (value) {
+ var next = Q.defer();
+ ends.resolve({
+ "head": value,
+ "tail": next.promise
+ });
+ ends.resolve = next.resolve;
+ },
+ "next": function () {
+ var result = Q.get(ends.promise, "head");
+ ends.promise = Q.get(ends.promise, "tail");
+ return result;
+ }
+ };
+}
+
+function makeStream(label) {
+ var result = Q.defer();
+ var queue = makeQueue();
+
+ var outstanding = 0;
+ var closed;
+
+ function send(value) {
+ outstanding++;
+ if (!closed) {
+ queue.send(value);
+ }
+ // ignore attempts to send values to a
+ // closed stream.
+ }
+
+ function close() {
+ closed = true;
+ // we are not actually closed
+ // until the last value gets emitted
+ }
+
+ function forEach(callback, thisp) {
+ Q.when(queue.next(), function (value) {
+ outstanding--;
+ // summon the next iteration
+ forEach(callback, thisp);
+ // handle this iteration
+ return Q.call(callback, thisp, value)
+ // resolve if this was the last
+ // value out the door.
+ .fin(function () {
+ if (outstanding === 0 && closed) {
+ console.log("CLOSED", label);
+ result.resolve();
+ }
+ });
+ })
+ .end();
+ return result.promise;
+ }
+
+ var promise = Q.makePromise({
+ "forEach": forEach,
+ "all": function () {
+ var object = [];
+ forEach(function (value) {
+ object.push(value);
+ });
+ return Q.when(result.promise, function () {
+ return object;
+ });
+ },
+ "map": function (callback, thisp) {
+ var map = makeStream(label + "|map");
+ forEach(function (value) {
+ Q.call(callback, thisp, value)
+ .then(map.send)
+ .end()
+ })
+ Q.when(result.promise, map.close);
+ return map.promise;
+ },
+ "filter": function (callback, thisp) {
+ var filter = makeStream(label + "|filter");
+ forEach(function (value) {
+ Q.call(callback, thisp, value)
+ .then(function (guard) {
+ if (guard) {
+ filter.send(value);
+ }
+ })
+ .end()
+ })
+ Q.when(result.promise, filter.close);
+ return filter.promise;
+ },
+ "reduce": function (callback, basis, thisp) {
+ var i = 0;
+ forEach(function (value) {
+ basis = Q.when(basis, function (basis) {
+ return Q.call(callback, thisp, basis, value, i++, result.promise);
+ });
+ })
+ return Q.when(result.promise, function () {
+ return basis;
+ });
+ }
+ }, function fallback(op) {
+ var args = Array.prototype.slice.call(arguments);
+ return Q.send.apply(this, [result.promise].concat(args));
+ });
+
+ return {
+ send: send,
+ close: close,
+ promise: promise
+ };
+};
+
+var stream = makeStream("sequence");
+
+stream.promise
+.map(function (n) {
+ console.log("mapping", n);
+ return Q.delay(n, 0)
+ .fin(function () {
+ console.log("emitting", n);
+ })
+})
+.filter(function (n) {
+ console.log("filtering", n);
+ return n % 2;
+})
+.reduce(function (old, n) {
+ console.log('reducing', old, n);
+ return old + n;
+}, 0)
+.then(function (result) {
+ console.log("result", result);
+})
+.end()
+
+var n = 0;
+var handle = setInterval(function () {
+ stream.send(n++);
+ if (n === 10) {
+ console.log("CLOSING");
+ stream.close();
+ }
+}, 10);
+
+Q.when(stream.promise, function () {
+ clearInterval(handle);
+});
+
8 test/thenable.js
View
@@ -1,8 +1,8 @@
var Q = require('../q');
-exports['test ref assimilation'] = function (ASSERT, done) {
- Q.ref({
+exports['test resolve assimilation'] = function (ASSERT, done) {
+ Q.resolve({
"then": function (resolved, rejected) {
resolved(10);
}
@@ -40,8 +40,8 @@ exports['test when assimilation'] = function (ASSERT, done) {
.fin(done);
}
-exports['test ref assimilation and piplining'] = function (ASSERT, done) {
- Q.ref({
+exports['test resolve assimilation and piplining'] = function (ASSERT, done) {
+ Q.resolve({
"then": function (resolved, rejected) {
resolved([10]);
}
8 test/value-of.js
View
@@ -18,15 +18,15 @@ exports['test defer defer'] = function (ASSERT) {
ASSERT.ok(Q.isResolved(d.promise), 'resolved');
};
-exports['test ref undefined'] = function (ASSERT) {
- var r = Q.ref();
+exports['test resolve undefined'] = function (ASSERT) {
+ var r = Q.resolve();
ASSERT.ok(Q.isResolved(r), 'isResolved');
ASSERT.equal(r.valueOf(), undefined, 'valueOf');
};
-exports['test ref defined'] = function (ASSERT) {
+exports['test resolve defined'] = function (ASSERT) {
var o = {};
- var r = Q.ref(o);
+ var r = Q.resolve(o);
ASSERT.ok(Q.isResolved(r), 'isResolved');
ASSERT.ok(r.valueOf() === o, 'valueOf');
};

No commit comments for this range

Something went wrong with that request. Please try again.