zotero/resource/q.js

1663 lines
52 KiB
JavaScript
Raw Normal View History

2012-06-03 04:43:42 +00:00
// vim:ts=4:sts=4:sw=4:
/*jshint browser: true, node: true,
curly: true, eqeqeq: true, noarg: true, nonew: true, trailing: true,
undef: true */
2012-12-11 08:22:26 +00:00
/*global define: false, Q: true, setImmediate: false,
ReturnValue: false, cajaVM: false, ses: false, bootstrap: false */
2012-06-03 04:43:42 +00:00
/*!
*
* 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.
*/
(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.
2012-12-11 08:22:26 +00:00
// Montage Require
if (typeof bootstrap === "function") {
bootstrap("promise", definition);
2012-06-03 04:43:42 +00:00
// CommonJS
} else if (typeof exports === "object") {
definition(void 0, exports);
2012-12-11 08:22:26 +00:00
// RequireJS
} else if (typeof define === "function") {
define(definition);
2012-06-03 04:43:42 +00:00
// SES (Secure EcmaScript)
} else if (typeof ses !== "undefined") {
if (!ses.ok()) {
return;
} else {
ses.makeQ = function () {
var Q = {};
return definition(void 0, Q);
};
}
// Mozilla JSM
} else if (~String(this).indexOf('BackstagePass')) {
EXPORTED_SYMBOLS = ["Q"];
// Q expects an implementation of setTimeout
setTimeout = new function() {
// We need to maintain references to running nsITimers. Otherwise, they can
// get garbage collected before they fire.
var _runningTimers = [];
return function setTimeout(func, ms) {
2012-07-30 06:37:39 +00:00
var useMethodjit = Components.utils.methodjit,
timer = Components.classes["@mozilla.org/timer;1"].
createInstance(Components.interfaces.nsITimer);
timer.initWithCallback({"notify":function() {
2012-07-30 06:37:39 +00:00
Components.utils.methodjit = useMethodjit;
// Remove timer from array so it can be garbage collected
_runningTimers.splice(_runningTimers.indexOf(timer), 1);
// Execute callback function
try {
func();
} catch(err) {
// Rethrow errors that occur so that they appear in the error
// console with the appropriate name and line numbers. While the
// the errors appear without this, the line numbers get eaten.
var scriptError = Components.classes["@mozilla.org/scripterror;1"]
.createInstance(Components.interfaces.nsIScriptError);
scriptError.init(
err.message || err.toString(),
err.fileName || err.filename || null,
null,
err.lineNumber || null,
null,
scriptError.errorFlag,
'component javascript'
);
Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService)
.logMessage(scriptError);
}
}}, ms, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
_runningTimers.push(timer);
}
};
definition(void 0, Q = {});
2012-12-11 08:22:26 +00:00
2012-06-03 04:43:42 +00:00
// <script>
} else {
definition(void 0, Q = {});
}
})(function (require, exports) {
"use strict";
2012-12-11 08:22:26 +00:00
// All code after this point will be filtered from stack traces reported
// by Q.
var qStartingLine = captureLine();
var qFileName;
2012-06-03 04:43:42 +00:00
// 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 setImmediate === "function") {
2012-12-11 08:22:26 +00:00
// In IE10, or use https://github.com/NobleJS/setImmediate
2012-06-03 04:43:42 +00:00
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 dont 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 Millers 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) {
2012-12-11 08:22:26 +00:00
return function () {
2012-06-03 04:43:42 +00:00
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;
}
2012-12-11 08:22:26 +00:00
function isInternalFrame(fileName, frame) {
if (fileName !== qFileName) {
return false;
}
var line = frame.getLineNumber();
return line >= qStartingLine && line <= qEndingLine;
}
2012-06-03 04:43:42 +00:00
/*
* 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" &&
2012-12-11 08:22:26 +00:00
!isInternalFrame(fileName, frame)
2012-06-03 04:43:42 +00:00
);
});
};
var stack = objectWithStack.stack;
Error.prepareStackTrace = oldPrepareStackTrace;
return stack;
}
2012-12-11 08:22:26 +00:00
// discover own file name and line number range for filtering stack
// traces
function captureLine() {
if (Error.captureStackTrace) {
var fileName, lineNumber;
2012-06-03 04:43:42 +00:00
var oldPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = function (error, frames) {
2012-12-11 08:22:26 +00:00
fileName = frames[1].getFileName();
lineNumber = frames[1].getLineNumber();
2012-06-03 04:43:42 +00:00
};
// teases call of temporary prepareStackTrace
// JSHint and Closure Compiler generate known warnings here
/*jshint expr: true */
new Error().stack;
Error.prepareStackTrace = oldPrepareStackTrace;
2012-12-11 08:22:26 +00:00
qFileName = fileName;
return lineNumber;
}
2012-06-03 04:43:42 +00:00
}
2012-12-11 08:22:26 +00:00
function deprecate(callback, name, alternative) {
2012-06-03 04:43:42 +00:00
return function () {
2012-12-11 08:22:26 +00:00
if (typeof console !== "undefined" && typeof console.warn === "function") {
console.warn(name + " is deprecated, use " + alternative + " instead.", new Error("").stack);
2012-06-03 04:43:42 +00:00
}
2012-12-11 08:22:26 +00:00
return callback.apply(callback, arguments);
2012-06-03 04:43:42 +00:00
};
}
// 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.
2012-12-11 08:22:26 +00:00
var pending = [], progressListeners = [], value;
2012-06-03 04:43:42 +00:00
var deferred = object_create(defer.prototype);
var promise = object_create(makePromise.prototype);
2012-12-11 08:22:26 +00:00
promise.promiseSend = function (op, _, __, progress) {
2012-06-03 04:43:42 +00:00
var args = array_slice(arguments);
if (pending) {
pending.push(args);
2012-12-11 08:22:26 +00:00
if (op === "when" && progress) {
progressListeners.push(progress);
}
2012-06-03 04:43:42 +00:00
} 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;
2012-12-11 08:22:26 +00:00
progressListeners = void 0;
2012-06-03 04:43:42 +00:00
return value;
}
defend(promise);
deferred.promise = promise;
deferred.resolve = become;
deferred.reject = function (exception) {
return become(reject(exception));
};
2012-12-11 08:22:26 +00:00
deferred.notify = function () {
if (pending) {
var args = arguments;
array_reduce(progressListeners, function (undefined, progressListener) {
nextTick(function () {
progressListener.apply(void 0, args);
});
}, void 0);
}
};
2012-06-03 04:43:42 +00:00
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
2012-12-11 08:22:26 +00:00
* the resolve, reject, and notify functions for a deferred.
2012-06-03 04:43:42 +00:00
* @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();
2012-12-11 08:22:26 +00:00
fcall(
2012-06-03 04:43:42 +00:00
makePromise,
deferred.resolve,
2012-12-11 08:22:26 +00:00
deferred.reject,
deferred.notify
2012-06-03 04:43:42 +00:00
).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);
}
2012-12-11 08:22:26 +00:00
if (resolved) {
resolved(result);
}
2012-06-03 04:43:42 +00:00
};
if (valueOf) {
promise.valueOf = valueOf;
}
if (exception) {
promise.exception = exception;
}
defend(promise);
return promise;
}
// provide thenables, CommonJS/Promises/A
2012-12-11 08:22:26 +00:00
makePromise.prototype.then = function (fulfilled, rejected, progressed) {
return when(this, fulfilled, rejected, progressed);
};
makePromise.prototype.thenResolve = function (value) {
return when(this, function () { return value; });
2012-06-03 04:43:42 +00:00
};
// 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",
2012-12-11 08:22:26 +00:00
"catch", "finally", "fail", "fin", "progress", "end", "done",
"ncall", "napply", "nbind",
"npost", "ninvoke",
"nend"
2012-06-03 04:43:42 +00:00
],
2012-12-11 08:22:26 +00:00
function (undefined, name) {
2012-06-03 04:43:42 +00:00
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 its a fulfilled promise, the fulfillment value is nearer.
* If its 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 = [];
2012-12-11 08:22:26 +00:00
var errorsDisplayed;
function displayErrors() {
if (
!errorsDisplayed &&
typeof window !== "undefined" &&
!window.Touch &&
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);
}
errorsDisplayed = true;
2012-06-03 04:43:42 +00:00
}
/**
* 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);
}
2012-12-11 08:22:26 +00:00
}, function fallback() {
2012-06-03 04:43:42 +00:00
return reject(exception);
}, function valueOf() {
return this;
}, exception);
// note that the error has not been handled
2012-12-11 08:22:26 +00:00
displayErrors();
2012-06-03 04:43:42 +00:00
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;
}
2012-12-11 08:22:26 +00:00
// 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.
object = valueOf(object);
2012-06-03 04:43:42 +00:00
// assimilate thenables, CommonJS/Promises/A
if (object && typeof object.then === "function") {
2012-12-11 08:22:26 +00:00
var deferred = defer();
object.then(deferred.resolve, deferred.reject, deferred.notify);
return deferred.promise;
2012-06-03 04:43:42 +00:00
}
return makePromise({
2012-12-11 08:22:26 +00:00
"when": function () {
2012-06-03 04:43:42 +00:00
return object;
},
"get": function (name) {
return object[name];
},
"put": function (name, value) {
2012-12-11 08:22:26 +00:00
object[name] = value;
return object;
2012-06-03 04:43:42 +00:00
},
"del": function (name) {
2012-12-11 08:22:26 +00:00
delete object[name];
return object;
2012-06-03 04:43:42 +00:00
},
"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 () {}
2012-12-11 08:22:26 +00:00
}, function fallback() {
2012-06-03 04:43:42 +00:00
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;
}
2012-12-11 08:22:26 +00:00
}, function fallback() {
2012-06-03 04:43:42 +00:00
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.
*
2012-12-11 08:22:26 +00:00
* @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
* @param progressed function to be called on any progress notifications
2012-06-03 04:43:42 +00:00
* @return promise for the return value from the invoked callback
*/
exports.when = when;
2012-12-11 08:22:26 +00:00
function when(value, fulfilled, rejected, progressed) {
2012-06-03 04:43:42 +00:00
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);
}
}
2012-12-11 08:22:26 +00:00
var resolvedValue = resolve(value);
2012-06-03 04:43:42 +00:00
nextTick(function () {
2012-12-11 08:22:26 +00:00
resolvedValue.promiseSend("when", function (value) {
2012-06-03 04:43:42 +00:00
if (done) {
return;
}
done = true;
2012-12-11 08:22:26 +00:00
deferred.resolve(_fulfilled(value));
2012-06-03 04:43:42 +00:00
}, function (exception) {
if (done) {
return;
}
done = true;
2012-12-11 08:22:26 +00:00
2012-06-03 04:43:42 +00:00
deferred.resolve(_rejected(exception));
});
});
2012-12-11 08:22:26 +00:00
// Progress listeners need to be attached in the current tick.
if (progressed) {
resolvedValue.promiseSend("when", void 0, void 0, progressed);
}
2012-06-03 04:43:42 +00:00
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) {
2012-12-11 08:22:26 +00:00
return when(promise, function (valuesOrPromises) {
return all(valuesOrPromises).then(function (values) {
return fulfilled.apply(void 0, values);
});
2012-06-03 04:43:42 +00:00
}, 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);
}
2012-12-11 08:22:26 +00:00
/**
* The promised function decorator ensures that any promise arguments
* are resolved and passed as values (`this` is also resolved and passed
* as a value). It will also ensure that the result of a function is
* always a promise.
*
* @example
* var add = Q.promised(function (a, b) {
* return a + b;
* });
* add(Q.resolve(a), Q.resolve(B));
*
* @param {function} callback The function to decorate
* @returns {function} a function that has been decorated.
*/
exports.promised = promised;
function promised(callback) {
return function () {
return all([this, all(arguments)]).spread(function (self, args) {
return callback.apply(self, args);
});
};
}
2012-06-03 04:43:42 +00:00
/**
* 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) {
2012-12-11 08:22:26 +00:00
if (isFulfilled(promise)) {
promises[index] = valueOf(promise);
2012-06-03 04:43:42 +00:00
if (--countDown === 0) {
deferred.resolve(promises);
}
2012-12-11 08:22:26 +00:00
} else {
when(promise, function (value) {
promises[index] = value;
if (--countDown === 0) {
deferred.resolve(promises);
}
})
.fail(deferred.reject);
}
2012-06-03 04:43:42 +00:00
}, 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);
}
2012-12-11 08:22:26 +00:00
/**
* Attaches a listener that can respond to progress notifications from a
* promise's originating deferred. This listener receives the exact arguments
* passed to ``deferred.notify``.
* @param {Any*} promise for something
* @param {Function} callback to receive any progress notifications
* @returns the given promise, unchanged
*/
exports.progress = progress;
function progress(promise, progressed) {
when(promise, void 0, void 0, progressed);
return promise;
}
2012-06-03 04:43:42 +00:00
/**
* 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
*/
2012-12-11 08:22:26 +00:00
exports.end = deprecate(done, "end", "done"); // XXX deprecated, use done
exports.done = done;
function done(promise, fulfilled, rejected, progress) {
function onUnhandledError(error) {
2012-06-03 04:43:42 +00:00
// 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
2012-12-11 08:22:26 +00:00
// the stack trace of the promise we are ``done``ing. See #57.
var errorStackFrames;
if (
Error.captureStackTrace &&
typeof error === "object" &&
(errorStackFrames = getStackFrames(error))
) {
2012-06-03 04:43:42 +00:00
var promiseStackFrames = getStackFrames(promise);
2012-12-11 08:22:26 +00:00
// Check to make sure the stack trace hasn't already been
// rendered (possibly by us).
if (typeof errorStackFrames !== "string") {
var combinedStackFrames = errorStackFrames.concat(
"From previous event:",
promiseStackFrames
);
error.stack = formatStackTrace(error, combinedStackFrames);
}
2012-06-03 04:43:42 +00:00
}
2012-12-11 08:22:26 +00:00
if (exports.onerror) {
exports.onerror(error);
} else {
throw error;
}
2012-06-03 04:43:42 +00:00
});
2012-12-11 08:22:26 +00:00
}
// Avoid unnecessary `nextTick`ing via an unnecessary `when`.
var promiseToHandle = fulfilled || rejected || progress ?
when(promise, fulfilled, rejected, progress) :
promise;
fail(promiseToHandle, onUnhandledError);
2012-06-03 04:43:42 +00:00
}
/**
* 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();
2012-07-10 01:01:29 +00:00
var timeoutId = setTimeout(function () {
deferred.reject(new Error("Timed out after " + ms + " ms"));
2012-06-03 04:43:42 +00:00
}, ms);
2012-07-10 01:01:29 +00:00
when(promise, function (value) {
clearTimeout(timeoutId);
deferred.resolve(value);
}, deferred.reject);
2012-06-03 04:43:42 +00:00
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.
*
2012-12-11 08:22:26 +00:00
* var FS = (require)("fs");
2012-06-03 04:43:42 +00:00
* 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.
*
2012-12-11 08:22:26 +00:00
* var FS = (require)("fs");
2012-06-03 04:43:42 +00:00
* 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)
2012-12-11 08:22:26 +00:00
* .done()
2012-06-03 04:43:42 +00:00
*
*/
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);
}
2012-12-11 08:22:26 +00:00
exports.nend = nend;
function nend(promise, nodeback) {
if (nodeback) {
promise.then(function (value) {
nextTick(function () {
nodeback(null, value);
});
}, function (error) {
nextTick(function () {
nodeback(error);
});
});
} else {
return promise;
}
}
// All code before this point will be filtered from stack traces.
var qEndingLine = captureLine();
2012-06-03 04:43:42 +00:00
});