commit c4cb78331797998db17784c15bf520a553676d89
parent 4e5d4281f1b197dcdf1368e6d879735f861243b4
Author: Simon Kornblith <simon@simonster.com>
Date: Sun, 3 Jun 2012 00:43:42 -0400
Add q promise library (as jsm)
Diffstat:
| A | resource/q.jsm | | | 1494 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 1494 insertions(+), 0 deletions(-)
diff --git a/resource/q.jsm b/resource/q.jsm
@@ -0,0 +1,1494 @@
+// vim:ts=4:sts=4:sw=4:
+/*jshint browser: true, node: true,
+ curly: true, eqeqeq: true, noarg: true, nonew: true, trailing: true,
+ undef: true */
+/*global define: false, Q: true, msSetImmediate: false, setImmediate: false,
+ ReturnValue: false, cajaVM: false, ses: false */
+/*!
+ *
+ * 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
+ *
+ * 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.
+ *
+ * With formatStackTrace and formatSourcePosition functions
+ * Copyright 2006-2008 the V8 project authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var EXPORTED_SYMBOLS = ["Q"];
+var setTimeout = new function() {
+ var _runningTimers = [];
+ return function setTimeout(func, ms) {
+ var timer = Components.classes["@mozilla.org/timer;1"].
+ createInstance(Components.interfaces.nsITimer);
+ var timerCallback = {"notify":function() {
+ // remove timer from global scope, so it can be garbage collected
+ _runningTimers.splice(_runningTimers.indexOf(timer), 1);
+ // execute callback function
+ try {
+ func();
+ } catch(err) {
+ var scriptError = Components.classes["@mozilla.org/scripterror;1"]
+ .createInstance(Components.interfaces.nsIScriptError);
+ scriptError.init(
+ err.message ? err.message : err.toString(),
+ err.fileName ? err.fileName : (err.filename ? err.filename : null),
+ null,
+ err.lineNumber ? err.lineNumber : null,
+ null,
+ scriptError['errorFlag'],
+ 'component javascript'
+ );
+ Components.classes["@mozilla.org/consoleservice;1"]
+ .getService(Components.interfaces.nsIConsoleService)
+ .logMessage(scriptError);
+ }
+ }};
+ timer.initWithCallback(timerCallback, ms, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+ }
+};
+
+(function (definition) {
+ // Turn off strict mode for this function so we can assign to global.Q
+ /*jshint strict: false*/
+
+ // This file will function properly as a <script> tag, or a module
+ // using CommonJS and NodeJS or RequireJS module formats. In
+ // Common/Node/RequireJS, the module exports the Q API and when
+ // executed as a simple <script>, it creates a Q global instead.
+
+ // RequireJS
+ if (typeof define === "function") {
+ define(definition);
+
+ // CommonJS
+ } else if (typeof exports === "object") {
+ 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(void 0, Q = {});
+ }
+
+})(function (require, exports) {
+"use strict";
+
+// shims
+
+// used for fallback "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
+ nextTick = process.nextTick;
+} else if (typeof msSetImmediate === "function") {
+ // IE 10 only, at the moment
+ // And yes, ``bind``ing to ``window`` is necessary O_o.
+ nextTick = msSetImmediate.bind(window);
+} else if (typeof setImmediate === "function") {
+ // https://github.com/NobleJS/setImmediate
+ nextTick = setImmediate;
+} else if (typeof MessageChannel !== "undefined") {
+ // modern browsers
+ // http://www.nonblocking.io/2011/06/windownexttick.html
+ var channel = new MessageChannel();
+ // linked list of tasks (single, with head node)
+ var head = {}, tail = head;
+ channel.port1.onmessage = function () {
+ head = head.next;
+ var task = head.task;
+ delete head.task;
+ task();
+ };
+ nextTick = function (task) {
+ tail = tail.next = {task: task};
+ channel.port2.postMessage(0);
+ };
+} else {
+ // old browsers
+ nextTick = function (task) {
+ setTimeout(task, 0);
+ };
+}
+
+// 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 array_slice = uncurryThis(Array.prototype.slice);
+
+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 (index in this) {
+ basis = this[index++];
+ break;
+ }
+ if (++index >= length) {
+ throw new TypeError();
+ }
+ } while (1);
+ }
+ // reduce
+ for (; index < length; index++) {
+ // account for the possibility that the array is sparse
+ if (index in this) {
+ basis = callback(basis, this[index], index);
+ }
+ }
+ return basis;
+ }
+);
+
+var array_indexOf = uncurryThis(
+ Array.prototype.indexOf || function (value) {
+ // not a very good shim, but good enough for our one use of it
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+);
+
+var array_map = uncurryThis(
+ Array.prototype.map || function (callback, thisp) {
+ var self = this;
+ var collect = [];
+ array_reduce(self, function (undefined, value, index) {
+ collect.push(callback.call(thisp, value, index, self));
+ }, void 0);
+ return collect;
+ }
+);
+
+var object_create = Object.create || function (prototype) {
+ function Type() { }
+ Type.prototype = prototype;
+ return new Type();
+};
+
+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;
+
+// generator related shims
+
+function isStopIteration(exception) {
+ return (
+ object_toString(exception) === "[object StopIteration]" ||
+ exception instanceof QReturnValue
+ );
+}
+
+var QReturnValue;
+if (typeof ReturnValue !== "undefined") {
+ QReturnValue = ReturnValue;
+} else {
+ QReturnValue = function (value) {
+ this.value = value;
+ };
+}
+
+// long stack traces
+
+function formatStackTrace(error, frames) {
+ var lines = [];
+ try {
+ lines.push(error.toString());
+ } catch (e) {
+ try {
+ lines.push("<error: " + e + ">");
+ } catch (ee) {
+ lines.push("<error>");
+ }
+ }
+ for (var i = 0; i < frames.length; i++) {
+ var frame = frames[i];
+ var line;
+
+ // <Inserted by @domenic>
+ if (typeof frame === "string") {
+ lines.push(frame);
+ // </Inserted by @domenic>
+ } else {
+ try {
+ line = formatSourcePosition(frame);
+ } catch (e) {
+ try {
+ line = "<error: " + e + ">";
+ } catch (ee) {
+ // Any code that reaches this point is seriously nasty!
+ line = "<error>";
+ }
+ }
+ lines.push(" at " + line);
+ }
+ }
+ return lines.join("\n");
+}
+
+function formatSourcePosition(frame) {
+ var fileLocation = "";
+ if (frame.isNative()) {
+ fileLocation = "native";
+ } else if (frame.isEval()) {
+ fileLocation = "eval at " + frame.getEvalOrigin();
+ } else {
+ var fileName = frame.getFileName();
+ if (fileName) {
+ fileLocation += fileName;
+ var lineNumber = frame.getLineNumber();
+ if (lineNumber !== null) {
+ fileLocation += ":" + lineNumber;
+ var columnNumber = frame.getColumnNumber();
+ if (columnNumber) {
+ fileLocation += ":" + columnNumber;
+ }
+ }
+ }
+ }
+ if (!fileLocation) {
+ fileLocation = "unknown source";
+ }
+ var line = "";
+ var functionName = frame.getFunction().name;
+ var addPrefix = true;
+ var isConstructor = frame.isConstructor();
+ var isMethodCall = !(frame.isToplevel() || isConstructor);
+ if (isMethodCall) {
+ var methodName = frame.getMethodName();
+ line += frame.getTypeName() + ".";
+ if (functionName) {
+ line += functionName;
+ if (methodName && (methodName !== functionName)) {
+ line += " [as " + methodName + "]";
+ }
+ } else {
+ line += methodName || "<anonymous>";
+ }
+ } else if (isConstructor) {
+ line += "new " + (functionName || "<anonymous>");
+ } else if (functionName) {
+ line += functionName;
+ } else {
+ line += fileLocation;
+ addPrefix = false;
+ }
+ if (addPrefix) {
+ line += " (" + fileLocation + ")";
+ }
+ return line;
+}
+
+/*
+ * Retrieves an array of structured stack frames parsed from the ``stack``
+ * property of a given object.
+ *
+ * @param objectWithStack {Object} an object with a ``stack`` property: usually
+ * an error or promise.
+ *
+ * @returns an array of stack frame objects. For more information, see
+ * [V8's JavaScript stack trace API documentation](http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi).
+ */
+function getStackFrames(objectWithStack) {
+ var oldPrepareStackTrace = Error.prepareStackTrace;
+
+ Error.prepareStackTrace = function (error, frames) {
+ // Filter out frames from the innards of Node and Q.
+ return frames.filter(function (frame) {
+ var fileName = frame.getFileName();
+ return (
+ fileName !== "module.js" &&
+ fileName !== "node.js" &&
+ fileName !== qFileName
+ );
+ });
+ };
+
+ var stack = objectWithStack.stack;
+
+ Error.prepareStackTrace = oldPrepareStackTrace;
+
+ return stack;
+}
+
+// discover own file name for filtering stack traces
+var qFileName;
+if (Error.captureStackTrace) {
+ qFileName = (function () {
+ var fileName;
+
+ var oldPrepareStackTrace = Error.prepareStackTrace;
+
+ Error.prepareStackTrace = function (error, frames) {
+ fileName = frames[0].getFileName();
+ };
+
+ // teases call of temporary prepareStackTrace
+ // JSHint and Closure Compiler generate known warnings here
+ /*jshint expr: true */
+ new Error().stack;
+
+ Error.prepareStackTrace = oldPrepareStackTrace;
+
+ return fileName;
+ })();
+}
+
+function deprecate(fn, name, alternative){
+ return function () {
+ if (typeof console !== "undefined" && typeof console.warn === "function"){
+ console.warn(name + " is deprecated, use " + alternative + " instead.");
+ }
+ return fn.apply(fn,arguments);
+ };
+}
+
+// end of shims
+// beginning of real work
+
+/**
+ * Performs a task in a future turn of the event loop.
+ * @param {Function} task
+ */
+exports.nextTick = nextTick;
+
+/**
+ * Constructs a {promise, resolve} 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.
+ */
+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
+ // element of the pending array is itself an array of complete arguments to
+ // forward to the resolved promise. We coerce the resolution value to a
+ // promise using the ref promise because it handles both fully
+ // resolved values and other promises gracefully.
+ var pending = [], value;
+
+ var deferred = object_create(defer.prototype);
+ var promise = object_create(makePromise.prototype);
+
+ promise.promiseSend = function () {
+ var args = array_slice(arguments);
+ if (pending) {
+ pending.push(args);
+ } else {
+ nextTick(function () {
+ value.promiseSend.apply(value, args);
+ });
+ }
+ };
+
+ promise.valueOf = function () {
+ if (pending) {
+ return promise;
+ }
+ return value.valueOf();
+ };
+
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(promise, defer);
+ }
+
+ function become(resolvedValue) {
+ if (!pending) {
+ return;
+ }
+ value = resolve(resolvedValue);
+ array_reduce(pending, function (undefined, pending) {
+ nextTick(function () {
+ value.promiseSend.apply(value, pending);
+ });
+ }, void 0);
+ pending = void 0;
+ return value;
+ }
+
+ defend(promise);
+
+ deferred.promise = promise;
+ deferred.resolve = become;
+ deferred.reject = function (exception) {
+ return become(reject(exception));
+ };
+
+ return deferred;
+}
+
+/**
+ * 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_slice(arguments, 1));
+ } else {
+ self.resolve(value);
+ }
+ };
+};
+// XXX deprecated
+defer.prototype.node = deprecate(defer.prototype.makeNodeResolver, "node", "makeNodeResolver");
+
+/**
+ * @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();
+ call(
+ makePromise,
+ void 0,
+ deferred.resolve,
+ deferred.reject
+ ).fail(deferred.reject);
+ return deferred.promise;
+}
+
+/**
+ * Constructs a Promise with a promise descriptor object and optional fallback
+ * function. The descriptor contains methods like when(rejected), get(name),
+ * put(name, value), post(name, args), and delete(name), which all
+ * return either a value, a promise for a value, or a rejection. The fallback
+ * accepts the operation name, a resolver, and any further arguments that would
+ * have been forwarded to the appropriate method above had a method been
+ * provided with the proper name. The API makes no guarantees about the nature
+ * of the returned object, apart from that it is usable whereever promises are
+ * bought and sold.
+ */
+exports.makePromise = makePromise;
+function makePromise(descriptor, fallback, valueOf, exception) {
+ if (fallback === void 0) {
+ fallback = function (op) {
+ return reject(new Error("Promise does not support operation: " + op));
+ };
+ }
+
+ var promise = object_create(makePromise.prototype);
+
+ promise.promiseSend = function (op, resolved /* ...args */) {
+ var args = array_slice(arguments, 2);
+ var result;
+ try {
+ if (descriptor[op]) {
+ result = descriptor[op].apply(promise, args);
+ } else {
+ result = fallback.apply(promise, [op].concat(args));
+ }
+ } catch (exception) {
+ result = reject(exception);
+ }
+ resolved(result);
+ };
+
+ if (valueOf) {
+ promise.valueOf = valueOf;
+ }
+
+ if (exception) {
+ promise.exception = exception;
+ }
+
+ defend(promise);
+
+ return promise;
+}
+
+// provide thenables, CommonJS/Promises/A
+makePromise.prototype.then = function (fulfilled, rejected) {
+ return when(this, fulfilled, rejected);
+};
+
+// Chainable methods
+array_reduce(
+ [
+ "isResolved", "isFulfilled", "isRejected",
+ "when", "spread", "send",
+ "get", "put", "del",
+ "post", "invoke",
+ "keys",
+ "apply", "call", "bind",
+ "fapply", "fcall", "fbind",
+ "all", "allResolved",
+ "view", "viewInfo",
+ "timeout", "delay",
+ "catch", "finally", "fail", "fin", "end"
+ ],
+ function (prev, name) {
+ makePromise.prototype[name] = function () {
+ return exports[name].apply(
+ exports,
+ [this].concat(array_slice(arguments))
+ );
+ };
+ },
+ void 0
+);
+
+makePromise.prototype.toSource = function () {
+ return this.toString();
+};
+
+makePromise.prototype.toString = function () {
+ return "[object Promise]";
+};
+
+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)
+ // generates a known JSHint "constructor invocation without new" warning
+ // supposed to be fixed, but isn't? https://github.com/jshint/jshint/issues/392
+ /*jshint newcap: false */
+ if (Object(value) !== value) {
+ return value;
+ } else {
+ return value.valueOf();
+ }
+}
+
+/**
+ * @returns whether the given object is a promise.
+ * Otherwise it is a fulfilled value.
+ */
+exports.isPromise = isPromise;
+function isPromise(object) {
+ return object && typeof object.promiseSend === "function";
+}
+
+/**
+ * @returns whether the given object is a resolved promise.
+ */
+exports.isResolved = isResolved;
+function isResolved(object) {
+ return isFulfilled(object) || isRejected(object);
+}
+
+/**
+ * @returns whether the given object is a value or fulfilled
+ * promise.
+ */
+exports.isFulfilled = isFulfilled;
+function isFulfilled(object) {
+ return !isPromise(valueOf(object));
+}
+
+/**
+ * @returns whether the given object is a rejected promise.
+ */
+exports.isRejected = isRejected;
+function isRejected(object) {
+ object = valueOf(object);
+ return isPromise(object) && 'exception' in object;
+}
+
+var rejections = [];
+var errors = [];
+if (typeof window !== "undefined" && window.console) {
+ // This promise library consumes exceptions thrown in handlers so
+ // they can be handled by a subsequent promise. The rejected
+ // promises get added to this array when they are created, and
+ // removed when they are handled.
+ console.log("Should be empty:", errors);
+}
+
+/**
+ * Constructs a rejected promise.
+ * @param exception value describing the failure
+ */
+exports.reject = reject;
+function reject(exception) {
+ exception = exception || new Error();
+ var rejection = makePromise({
+ "when": function (rejected) {
+ // note that the error has been handled
+ if (rejected) {
+ var at = array_indexOf(rejections, this);
+ if (at !== -1) {
+ errors.splice(at, 1);
+ rejections.splice(at, 1);
+ }
+ }
+ return rejected ? rejected(exception) : reject(exception);
+ }
+ }, function fallback(op) {
+ return reject(exception);
+ }, function valueOf() {
+ return this;
+ }, exception);
+ // note that the error has not been handled
+ rejections.push(rejection);
+ errors.push(exception);
+ return rejection;
+}
+
+/**
+ * Constructs a promise for an immediate reference.
+ * @param value immediate reference
+ */
+exports.begin = resolve; // XXX experimental
+exports.resolve = resolve;
+exports.ref = deprecate(resolve, "ref", "resolve"); // XXX deprecated, use resolve
+function resolve(object) {
+ // If the object is already a Promise, return it directly. This enables
+ // the resolve function to both be used to created references from objects,
+ // but to tolerably coerce non-promises to promises.
+ if (isPromise(object)) {
+ return object;
+ }
+ // assimilate thenables, CommonJS/Promises/A
+ if (object && typeof object.then === "function") {
+ var result = defer();
+ object.then(result.resolve, result.reject);
+ return result.promise;
+ }
+ return makePromise({
+ "when": function (rejected) {
+ return object;
+ },
+ "get": function (name) {
+ return object[name];
+ },
+ "put": function (name, value) {
+ return object[name] = value;
+ },
+ "del": function (name) {
+ return delete object[name];
+ },
+ "post": function (name, value) {
+ return object[name].apply(object, value);
+ },
+ "apply": function (self, args) {
+ return object.apply(self, args);
+ },
+ "fapply": function (args) {
+ return object.apply(void 0, args);
+ },
+ "viewInfo": function () {
+ var on = object;
+ var properties = {};
+
+ function fixFalsyProperty(name) {
+ if (!properties[name]) {
+ properties[name] = typeof on[name];
+ }
+ }
+
+ while (on) {
+ Object.getOwnPropertyNames(on).forEach(fixFalsyProperty);
+ on = Object.getPrototypeOf(on);
+ }
+ return {
+ "type": typeof object,
+ "properties": properties
+ };
+ },
+ "keys": function () {
+ return object_keys(object);
+ }
+ }, void 0, function valueOf() {
+ return object;
+ });
+}
+
+/**
+ * Annotates an object such that it will never be
+ * transferred away from this process over any promise
+ * communication channel.
+ * @param object
+ * @returns promise a wrapping of that object that
+ * additionally responds to the "isDef" message
+ * without a rejection.
+ */
+exports.master = master;
+function master(object) {
+ return makePromise({
+ "isDef": function () {}
+ }, function fallback(op) {
+ var args = array_slice(arguments);
+ return send.apply(void 0, [object].concat(args));
+ }, function () {
+ return valueOf(object);
+ });
+}
+
+exports.viewInfo = viewInfo;
+function viewInfo(object, info) {
+ object = resolve(object);
+ if (info) {
+ return makePromise({
+ "viewInfo": function () {
+ return info;
+ }
+ }, function fallback(op) {
+ var args = array_slice(arguments);
+ return send.apply(void 0, [object].concat(args));
+ }, function () {
+ return valueOf(object);
+ });
+ } else {
+ return send(object, "viewInfo");
+ }
+}
+
+exports.view = view;
+function view(object) {
+ return viewInfo(object).when(function (info) {
+ var view;
+ if (info.type === "function") {
+ view = function () {
+ return apply(object, void 0, arguments);
+ };
+ } else {
+ view = {};
+ }
+ var properties = info.properties || {};
+ object_keys(properties).forEach(function (name) {
+ if (properties[name] === "function") {
+ view[name] = function () {
+ return post(object, name, arguments);
+ };
+ }
+ });
+ return resolve(view);
+ });
+}
+
+/**
+ * Registers an observer on a promise.
+ *
+ * Guarantees:
+ *
+ * 1. that fulfilled and rejected will be called only once.
+ * 2. that either the fulfilled callback or the rejected callback will be
+ * called, but not both.
+ * 3. that fulfilled and rejected will not be called in this turn.
+ *
+ * @param value promise or immediate reference to observe
+ * @param fulfilled function to be called with the fulfilled value
+ * @param rejected function to be called with the rejection exception
+ * @return promise for the return value from the invoked callback
+ */
+exports.when = when;
+function when(value, fulfilled, rejected) {
+ var deferred = defer();
+ var done = false; // ensure the untrusted promise makes at most a
+ // single call to one of the callbacks
+
+ function _fulfilled(value) {
+ try {
+ return fulfilled ? fulfilled(value) : value;
+ } catch (exception) {
+ return reject(exception);
+ }
+ }
+
+ function _rejected(exception) {
+ try {
+ return rejected ? rejected(exception) : reject(exception);
+ } catch (newException) {
+ return reject(newException);
+ }
+ }
+
+ nextTick(function () {
+ resolve(value).promiseSend("when", function (value) {
+ if (done) {
+ return;
+ }
+ done = true;
+ resolve(value).promiseSend("when", function (value) {
+ deferred.resolve(_fulfilled(value));
+ }, function (exception) {
+ deferred.resolve(_rejected(exception));
+ });
+ }, function (exception) {
+ if (done) {
+ return;
+ }
+ done = true;
+ deferred.resolve(_rejected(exception));
+ });
+ });
+
+ return deferred.promise;
+}
+
+/**
+ * Spreads the values of a promised array of arguments into the
+ * fulfillment callback.
+ * @param fulfilled callback that receives variadic arguments from the
+ * promised array
+ * @param rejected callback that receives the exception if the promise
+ * is rejected.
+ * @returns a promise for the return value or thrown exception of
+ * either callback.
+ */
+exports.spread = spread;
+function spread(promise, fulfilled, rejected) {
+ return when(promise, function (values) {
+ return fulfilled.apply(void 0, values);
+ }, rejected);
+}
+
+/**
+ * The async function is a decorator for generator functions, turning
+ * them into asynchronous generators. This presently only works in
+ * Firefox/Spidermonkey, however, this code does not cause syntax
+ * errors in older engines. This code should continue to work and
+ * will in fact improve over time as the language improves.
+ *
+ * Decorates a generator function such that:
+ * - it may yield promises
+ * - execution will continue when that promise is fulfilled
+ * - the value of the yield expression will be the fulfilled value
+ * - it returns a promise for the return value (when the generator
+ * stops iterating)
+ * - the decorated function returns a promise for the return value
+ * of the generator or the first rejected promise among those
+ * yielded.
+ * - if an error is thrown in the generator, it propagates through
+ * every following yield until it is caught, or until it escapes
+ * the generator function altogether, and is translated into a
+ * rejection for the promise returned by the decorated generator.
+ * - in present implementations of generators, when a generator
+ * function is complete, it throws ``StopIteration``, ``return`` is
+ * a syntax error in the presence of ``yield``, so there is no
+ * observable return value. There is a proposal[1] to add support
+ * for ``return``, which would permit the value to be carried by a
+ * ``StopIteration`` instance, in which case it would fulfill the
+ * promise returned by the asynchronous generator. This can be
+ * emulated today by throwing StopIteration explicitly with a value
+ * property.
+ *
+ * [1]: http://wiki.ecmascript.org/doku.php?id=strawman:async_functions#reference_implementation
+ *
+ */
+exports.async = async;
+function async(makeGenerator) {
+ return function () {
+ // when verb is "send", arg is a value
+ // when verb is "throw", arg is an exception
+ function continuer(verb, arg) {
+ var result;
+ try {
+ result = generator[verb](arg);
+ } catch (exception) {
+ if (isStopIteration(exception)) {
+ return exception.value;
+ } else {
+ return reject(exception);
+ }
+ }
+ return when(result, callback, errback);
+ }
+ var generator = makeGenerator.apply(this, arguments);
+ var callback = continuer.bind(continuer, "send");
+ var errback = continuer.bind(continuer, "throw");
+ return callback();
+ };
+}
+
+/**
+ * Throws a ReturnValue exception to stop an asynchronous generator.
+ * Only useful presently in Firefox/SpiderMonkey since generators are
+ * implemented.
+ * @param value the return value for the surrounding generator
+ * @throws ReturnValue exception with the value.
+ * @example
+ * Q.async(function () {
+ * var foo = yield getFooPromise();
+ * var bar = yield getBarPromise();
+ * Q.return(foo + bar);
+ * })
+ */
+exports['return'] = _return;
+function _return(value) {
+ throw new QReturnValue(value);
+}
+
+/**
+ * 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.
+ */
+exports.sender = deprecate(sender, "sender", "dispatcher"); // XXX deprecated, use dispatcher
+exports.Method = deprecate(sender, "Method", "dispatcher"); // XXX deprecated, use dispatcher
+function sender(op) {
+ return function (object) {
+ var args = array_slice(arguments, 1);
+ return send.apply(void 0, [object, op].concat(args));
+ };
+}
+
+/**
+ * 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.send = deprecate(send, "send", "dispatch"); // XXX deprecated, use dispatch
+function send(object, op) {
+ var deferred = defer();
+ 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(
+ object,
+ [op, deferred.resolve].concat(args)
+ );
+ });
+ return deferred.promise;
+}
+
+/**
+ * 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 = dispatcher("get");
+
+/**
+ * Sets the value of a property in a future turn.
+ * @param object promise or immediate reference for object object
+ * @param name name of property to set
+ * @param value new value of property
+ * @return promise for the return value
+ */
+exports.put = dispatcher("put");
+
+/**
+ * Deletes a property in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of property to delete
+ * @return promise for the return value
+ */
+exports["delete"] = // XXX experimental
+exports.del = dispatcher("del");
+
+/**
+ * Invokes a method in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of method to invoke
+ * @param value a value to post, typically an array of
+ * invocation arguments for promises that
+ * are ultimately backed with `resolve` values,
+ * as opposed to those backed with URLs
+ * wherein the posted value can be any
+ * JSON serializable object.
+ * @return promise for the return value
+ */
+// bound locally because it is used by other methods
+var post = exports.post = dispatcher("post");
+
+/**
+ * Invokes a method in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of method to invoke
+ * @param ...args array of invocation arguments
+ * @return promise for the return value
+ */
+exports.invoke = function (value, name) {
+ 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 thisp the `this` object for the call
+ * @param args array of application arguments
+ */
+// XXX deprecated, use fapply
+var apply = exports.apply = deprecate(dispatcher("apply"), "apply", "fapply");
+
+/**
+ * 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 thisp the `this` object for the call
+ * @param ...args array of application arguments
+ */
+// XXX deprecated, use fcall
+exports.call = deprecate(call, "call", "fcall");
+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["try"] = fcall; // XXX experimental
+exports.fcall = 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 thisp the `this` object for the call
+ * @param ...args array of application arguments
+ */
+exports.bind = deprecate(bind, "bind", "fbind"); // XXX deprecated, use fbind
+function bind(value, thisp) {
+ var args = array_slice(arguments, 2);
+ return function bound() {
+ var allArgs = args.concat(array_slice(arguments));
+ return apply(value, thisp, 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);
+ };
+}
+
+/**
+ * Requests the names of the owned properties of a promised
+ * object in a future turn.
+ * @param object promise or immediate reference for target object
+ * @return promise for the keys of the eventually resolved object
+ */
+exports.keys = dispatcher("keys");
+
+/**
+ * Turns an array of promises into a promise for an array. If any of
+ * the promises gets rejected, the whole array is rejected immediately.
+ * @param {Array*} an array (or promise for an array) of values (or
+ * promises for values)
+ * @returns a promise for an array of the corresponding values
+ */
+// By Mark Miller
+// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled
+exports.all = all;
+function all(promises) {
+ return when(promises, function (promises) {
+ var countDown = promises.length;
+ if (countDown === 0) {
+ return resolve(promises);
+ }
+ var deferred = defer();
+ array_reduce(promises, function (undefined, promise, index) {
+ when(promise, function (value) {
+ promises[index] = value;
+ if (--countDown === 0) {
+ deferred.resolve(promises);
+ }
+ })
+ .fail(deferred.reject);
+ }, void 0);
+ return deferred.promise;
+ });
+}
+
+/**
+ * Waits for all promises to be resolved, either fulfilled or
+ * rejected. This is distinct from `all` since that would stop
+ * waiting at the first rejection. The promise returned by
+ * `allResolved` will never be rejected.
+ * @param promises a promise for an array (or an array) of promises
+ * (or values)
+ * @return a promise for an array of promises
+ */
+exports.allResolved = allResolved;
+function allResolved(promises) {
+ return when(promises, function (promises) {
+ return when(all(array_map(promises, function (promise) {
+ return when(promise, noop, noop);
+ })), function () {
+ return array_map(promises, resolve);
+ });
+ });
+}
+
+/**
+ * Captures the failure of a promise, giving an oportunity to recover
+ * with a callback. If the given promise is fulfilled, the returned
+ * promise is fulfilled.
+ * @param {Any*} promise for something
+ * @param {Function} callback to fulfill the returned promise if the
+ * given promise is rejected
+ * @returns a promise for the return value of the callback
+ */
+exports["catch"] = // XXX experimental
+exports.fail = fail;
+function fail(promise, rejected) {
+ return when(promise, void 0, rejected);
+}
+
+/**
+ * Provides an opportunity to observe the rejection of a promise,
+ * regardless of whether the promise is fulfilled or rejected. Forwards
+ * the resolution to the returned promise when the callback is done.
+ * The callback can return a promise to defer completion.
+ * @param {Any*} promise
+ * @param {Function} callback to observe the resolution of the given
+ * promise, takes no arguments.
+ * @returns a promise for the resolution of the given promise when
+ * ``fin`` is done.
+ */
+exports["finally"] = // XXX experimental
+exports.fin = fin;
+function fin(promise, callback) {
+ return when(promise, function (value) {
+ return when(callback(), function () {
+ return value;
+ });
+ }, function (exception) {
+ return when(callback(), function () {
+ return reject(exception);
+ });
+ });
+}
+
+/**
+ * Terminates a chain of promises, forcing rejections to be
+ * thrown as exceptions.
+ * @param {Any*} promise at the end of a chain of promises
+ * @returns nothing
+ */
+exports.end = end; // XXX stopgap
+function end(promise) {
+ when(promise, void 0, function (error) {
+ // forward to a future turn so that ``when``
+ // does not catch it and turn it into a rejection.
+ nextTick(function () {
+ // If possible (that is, if in V8), transform the error stack
+ // trace by removing Node and Q cruft, then concatenating with
+ // the stack trace of the promise we are ``end``ing. See #57.
+ if (Error.captureStackTrace && "stack" in error) {
+ var errorStackFrames = getStackFrames(error);
+ var promiseStackFrames = getStackFrames(promise);
+
+ var combinedStackFrames = errorStackFrames.concat(
+ "From previous event:",
+ promiseStackFrames
+ );
+ error.stack = formatStackTrace(error, combinedStackFrames);
+ }
+
+ throw error;
+ });
+ });
+}
+
+/**
+ * Causes a promise to be rejected if it does not get fulfilled before
+ * some milliseconds time out.
+ * @param {Any*} promise
+ * @param {Number} milliseconds timeout
+ * @returns a promise for the resolution of the given promise if it is
+ * fulfilled before the timeout, otherwise rejected.
+ */
+exports.timeout = timeout;
+function timeout(promise, ms) {
+ var deferred = defer();
+ when(promise, deferred.resolve, deferred.reject);
+ setTimeout(function () {
+ deferred.reject(new Error("Timed out after " + ms + "ms"));
+ }, ms);
+ return deferred.promise;
+}
+
+/**
+ * Returns a promise for the given value (or promised value) after some
+ * milliseconds.
+ * @param {Any*} promise
+ * @param {Number} milliseconds
+ * @returns a promise for the resolution of the given promise after some
+ * time has elapsed.
+ */
+exports.delay = delay;
+function delay(promise, timeout) {
+ if (timeout === void 0) {
+ timeout = promise;
+ promise = void 0;
+ }
+ var deferred = defer();
+ setTimeout(function () {
+ deferred.resolve(promise);
+ }, timeout);
+ 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.
+ *
+ * var FS = require("fs");
+ * Q.napply(FS.readFile, FS, [__filename])
+ * .then(function (content) {
+ * })
+ *
+ */
+exports.napply = napply;
+function napply(callback, thisp, args) {
+ return nbind(callback, thisp).apply(void 0, args);
+}
+
+/**
+ * Passes a continuation to a Node function, which is called with a given
+ * `this` value and arguments provided individually, and returns a promise.
+ *
+ * var FS = require("fs");
+ * Q.ncall(FS.readFile, FS, __filename)
+ * .then(function (content) {
+ * })
+ *
+ */
+exports.ncall = ncall;
+function ncall(callback, thisp /*, ...args*/) {
+ 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 thisp = arguments[1];
+ var args = array_slice(arguments, 2);
+
+ var originalCallback = callback;
+ callback = function () {
+ var combinedArgs = args.concat(array_slice(arguments));
+ return originalCallback.apply(thisp, combinedArgs);
+ };
+ }
+ 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
+ fapply(callback, args)
+ .fail(deferred.reject);
+ return deferred.promise;
+ };
+}
+
+/**
+ * Calls a method of a Node-style object that accepts a Node-style
+ * callback with a given array of arguments, plus a provided callback.
+ * @param object an object that has the named method
+ * @param {String} name name of the method of object
+ * @param {Array} args arguments to pass to the method; the callback
+ * will be provided by Q and appended to these arguments.
+ * @returns a promise for the value or error
+ */
+exports.npost = npost;
+function npost(object, name, args) {
+ return napply(object[name], object, args);
+}
+
+/**
+ * Calls a method of a Node-style object that accepts a Node-style
+ * callback, forwarding the given variadic arguments, plus a provided
+ * callback argument.
+ * @param object an object that has the named method
+ * @param {String} name name of the method of object
+ * @param ...args arguments to pass to the method; the callback will
+ * be provided by Q and appended to these arguments.
+ * @returns a promise for the value or error
+ */
+exports.ninvoke = ninvoke;
+function ninvoke(object, name /*, ...args*/) {
+ var args = array_slice(arguments, 2);
+ return napply(object[name], object, args);
+}
+
+defend(exports);
+
+});