1803 lines
54 KiB
JavaScript
1803 lines
54 KiB
JavaScript
/* vim:ts=4:sts=4:sw=4: */
|
|
/*!
|
|
*
|
|
* Copyright 2009-2013 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.
|
|
*
|
|
*/
|
|
/*global -WeakMap */
|
|
"use strict";
|
|
|
|
var hasStacks = false;
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
hasStacks = !!e.stack;
|
|
}
|
|
|
|
// All code after this point will be filtered from stack traces reported
|
|
// by Q.
|
|
var qStartingLine = captureLine();
|
|
var qFileName;
|
|
|
|
var WeakMap = require("weak-map");
|
|
var iterate = require("pop-iterate");
|
|
var asap = require("asap");
|
|
|
|
function isObject(value) {
|
|
return value === Object(value);
|
|
}
|
|
|
|
// long stack traces
|
|
|
|
var STACK_JUMP_SEPARATOR = "From previous event:";
|
|
|
|
function makeStackTraceLong(error, promise) {
|
|
// If possible, transform the error stack trace by removing Node and Q
|
|
// cruft, then concatenating with the stack trace of `promise`. See #57.
|
|
if (hasStacks &&
|
|
promise.stack &&
|
|
typeof error === "object" &&
|
|
error !== null &&
|
|
error.stack &&
|
|
error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
|
|
) {
|
|
var stacks = [];
|
|
for (var p = promise; !!p && handlers.get(p); p = handlers.get(p).became) {
|
|
if (p.stack) {
|
|
stacks.unshift(p.stack);
|
|
}
|
|
}
|
|
stacks.unshift(error.stack);
|
|
|
|
var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n");
|
|
error.stack = filterStackString(concatedStacks);
|
|
}
|
|
}
|
|
|
|
function filterStackString(stackString) {
|
|
if (Q.isIntrospective) {
|
|
return stackString;
|
|
}
|
|
var lines = stackString.split("\n");
|
|
var desiredLines = [];
|
|
for (var i = 0; i < lines.length; ++i) {
|
|
var line = lines[i];
|
|
|
|
if (!isInternalFrame(line) && !isNodeFrame(line) && line) {
|
|
desiredLines.push(line);
|
|
}
|
|
}
|
|
return desiredLines.join("\n");
|
|
}
|
|
|
|
function isNodeFrame(stackLine) {
|
|
return stackLine.indexOf("(module.js:") !== -1 ||
|
|
stackLine.indexOf("(node.js:") !== -1;
|
|
}
|
|
|
|
function getFileNameAndLineNumber(stackLine) {
|
|
// Named functions: "at functionName (filename:lineNumber:columnNumber)"
|
|
// In IE10 function name can have spaces ("Anonymous function") O_o
|
|
var attempt1 = /at .+ \((.+):(\d+):(?:\d+)\)$/.exec(stackLine);
|
|
if (attempt1) {
|
|
return [attempt1[1], Number(attempt1[2])];
|
|
}
|
|
|
|
// Anonymous functions: "at filename:lineNumber:columnNumber"
|
|
var attempt2 = /at ([^ ]+):(\d+):(?:\d+)$/.exec(stackLine);
|
|
if (attempt2) {
|
|
return [attempt2[1], Number(attempt2[2])];
|
|
}
|
|
|
|
// Firefox style: "function@filename:lineNumber or @filename:lineNumber"
|
|
var attempt3 = /.*@(.+):(\d+)$/.exec(stackLine);
|
|
if (attempt3) {
|
|
return [attempt3[1], Number(attempt3[2])];
|
|
}
|
|
}
|
|
|
|
function isInternalFrame(stackLine) {
|
|
var fileNameAndLineNumber = getFileNameAndLineNumber(stackLine);
|
|
|
|
if (!fileNameAndLineNumber) {
|
|
return false;
|
|
}
|
|
|
|
var fileName = fileNameAndLineNumber[0];
|
|
var lineNumber = fileNameAndLineNumber[1];
|
|
|
|
return fileName === qFileName &&
|
|
lineNumber >= qStartingLine &&
|
|
lineNumber <= qEndingLine;
|
|
}
|
|
|
|
// discover own file name and line number range for filtering stack
|
|
// traces
|
|
function captureLine() {
|
|
if (!hasStacks) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
var lines = e.stack.split("\n");
|
|
var firstLine = lines[0].indexOf("@") > 0 ? lines[1] : lines[2];
|
|
var fileNameAndLineNumber = getFileNameAndLineNumber(firstLine);
|
|
if (!fileNameAndLineNumber) {
|
|
return;
|
|
}
|
|
|
|
qFileName = fileNameAndLineNumber[0];
|
|
return fileNameAndLineNumber[1];
|
|
}
|
|
}
|
|
|
|
function deprecate(callback, name, alternative) {
|
|
return function Q_deprecate() {
|
|
if (
|
|
typeof console !== "undefined" &&
|
|
typeof console.warn === "function"
|
|
) {
|
|
if (alternative) {
|
|
console.warn(
|
|
name + " is deprecated, use " + alternative + " instead.",
|
|
new Error("").stack
|
|
);
|
|
} else {
|
|
console.warn(
|
|
name + " is deprecated.",
|
|
new Error("").stack
|
|
);
|
|
}
|
|
}
|
|
return callback.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
// end of long stack traces
|
|
|
|
var handlers = new WeakMap();
|
|
|
|
function Q_getHandler(promise) {
|
|
var handler = handlers.get(promise);
|
|
if (!handler || !handler.became) {
|
|
return handler;
|
|
}
|
|
handler = follow(handler);
|
|
handlers.set(promise, handler);
|
|
return handler;
|
|
}
|
|
|
|
function follow(handler) {
|
|
if (!handler.became) {
|
|
return handler;
|
|
} else {
|
|
handler.became = follow(handler.became);
|
|
return handler.became;
|
|
}
|
|
}
|
|
|
|
var theViciousCycleError = new Error("Can't resolve a promise with itself");
|
|
var theViciousCycleRejection = Q_reject(theViciousCycleError);
|
|
var theViciousCycle = Q_getHandler(theViciousCycleRejection);
|
|
|
|
var thenables = new WeakMap();
|
|
|
|
/**
|
|
* Coerces a value to a promise. If the value is a promise, pass it through
|
|
* unaltered. If the value has a `then` method, it is presumed to be a promise
|
|
* but not one of our own, so it is treated as a “thenable” promise and this
|
|
* returns a promise that stands for it. Otherwise, this returns a promise that
|
|
* has already been fulfilled with the value.
|
|
* @param value promise, object with a then method, or a fulfillment value
|
|
* @returns {Promise} the same promise as given, or a promise for the given
|
|
* value
|
|
*/
|
|
module.exports = Q;
|
|
function Q(value) {
|
|
// 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 (Q_isPromise(value)) {
|
|
return value;
|
|
} else if (isThenable(value)) {
|
|
if (!thenables.has(value)) {
|
|
thenables.set(value, new Promise(new Thenable(value)));
|
|
}
|
|
return thenables.get(value);
|
|
} else {
|
|
return new Promise(new Fulfilled(value));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Controls whether or not long stack traces will be on
|
|
* @type {boolean}
|
|
*/
|
|
Q.longStackSupport = false;
|
|
|
|
/**
|
|
* Returns a promise that has been rejected with a reason, which should be an
|
|
* instance of `Error`.
|
|
* @param {Error} error reason for the failure.
|
|
* @returns {Promise} rejection
|
|
*/
|
|
Q.reject = Q_reject;
|
|
function Q_reject(error) {
|
|
return new Promise(new Rejected(error));
|
|
}
|
|
|
|
/**
|
|
* Constructs a {promise, resolve, reject} object.
|
|
*
|
|
* `resolve` is a callback to invoke with a more resolved value for the
|
|
* promise. To fulfill the promise, invoke `resolve` with any value that is
|
|
* not a thenable. To reject the promise, invoke `resolve` with a rejected
|
|
* thenable, or invoke `reject` with the reason directly. To resolve the
|
|
* promise to another thenable, thus putting it in the same state, invoke
|
|
* `resolve` with that other thenable.
|
|
*
|
|
* @returns {{promise, resolve, reject}} a deferred
|
|
*/
|
|
Q.defer = defer;
|
|
function defer() {
|
|
|
|
var handler = new Pending();
|
|
var promise = new Promise(handler);
|
|
var deferred = new Deferred(promise);
|
|
|
|
if (Q.longStackSupport && hasStacks) {
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
// NOTE: don't try to use `Error.captureStackTrace` or transfer the
|
|
// accessor around; that causes memory leaks as per GH-111. Just
|
|
// reify the stack trace as a string ASAP.
|
|
//
|
|
// At the same time, cut off the first line; it's always just
|
|
// "[object Promise]\n", as per the `toString`.
|
|
promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);
|
|
}
|
|
}
|
|
|
|
return deferred;
|
|
}
|
|
|
|
// TODO
|
|
/**
|
|
*/
|
|
Q.when = function Q_when(value, fulfilled, rejected, ms) {
|
|
return Q(value).then(fulfilled, rejected, ms);
|
|
};
|
|
|
|
/**
|
|
* 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.<Promise>} an array (or promise for an array) of values (or
|
|
* promises for values)
|
|
* @returns {Promise.<Array>} a promise for an array of the corresponding values
|
|
*/
|
|
// By Mark Miller
|
|
// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled
|
|
Q.all = Q_all;
|
|
function Q_all(questions) {
|
|
// XXX deprecated behavior
|
|
if (Q_isPromise(questions)) {
|
|
if (
|
|
typeof console !== "undefined" &&
|
|
typeof console.warn === "function"
|
|
) {
|
|
console.warn("Q.all no longer directly unwraps a promise. Use Q(array).all()");
|
|
}
|
|
return Q(questions).all();
|
|
}
|
|
var countDown = 0;
|
|
var deferred = defer();
|
|
var answers = Array(questions.length);
|
|
var estimates = [];
|
|
var estimate = -Infinity;
|
|
var setEstimate;
|
|
Array.prototype.forEach.call(questions, function Q_all_each(promise, index) {
|
|
var handler;
|
|
if (
|
|
Q_isPromise(promise) &&
|
|
(handler = Q_getHandler(promise)).state === "fulfilled"
|
|
) {
|
|
answers[index] = handler.value;
|
|
} else {
|
|
++countDown;
|
|
promise = Q(promise);
|
|
promise.then(
|
|
function Q_all_eachFulfilled(value) {
|
|
answers[index] = value;
|
|
if (--countDown === 0) {
|
|
deferred.resolve(answers);
|
|
}
|
|
},
|
|
deferred.reject
|
|
);
|
|
|
|
promise.observeEstimate(function Q_all_eachEstimate(newEstimate) {
|
|
var oldEstimate = estimates[index];
|
|
estimates[index] = newEstimate;
|
|
if (newEstimate > estimate) {
|
|
estimate = newEstimate;
|
|
} else if (oldEstimate === estimate && newEstimate <= estimate) {
|
|
// There is a 1/length chance that we will need to perform
|
|
// this O(length) walk, so amortized O(1)
|
|
computeEstimate();
|
|
}
|
|
if (estimates.length === questions.length && estimate !== setEstimate) {
|
|
deferred.setEstimate(estimate);
|
|
setEstimate = estimate;
|
|
}
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
function computeEstimate() {
|
|
estimate = -Infinity;
|
|
for (var index = 0; index < estimates.length; index++) {
|
|
if (estimates[index] > estimate) {
|
|
estimate = estimates[index];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (countDown === 0) {
|
|
deferred.resolve(answers);
|
|
}
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* @see Promise#allSettled
|
|
*/
|
|
Q.allSettled = Q_allSettled;
|
|
function Q_allSettled(questions) {
|
|
// XXX deprecated behavior
|
|
if (Q_isPromise(questions)) {
|
|
if (
|
|
typeof console !== "undefined" &&
|
|
typeof console.warn === "function"
|
|
) {
|
|
console.warn("Q.allSettled no longer directly unwraps a promise. Use Q(array).allSettled()");
|
|
}
|
|
return Q(questions).allSettled();
|
|
}
|
|
return Q_all(questions.map(function Q_allSettled_each(promise) {
|
|
promise = Q(promise);
|
|
function regardless() {
|
|
return promise.inspect();
|
|
}
|
|
return promise.then(regardless, regardless);
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Returns a promise for the given value (or promised value), some
|
|
* milliseconds after it resolved. Passes rejections immediately.
|
|
* @param {Any*} promise
|
|
* @param {Number} milliseconds
|
|
* @returns a promise for the resolution of the given promise after milliseconds
|
|
* time has elapsed since the resolution of the given promise.
|
|
* If the given promise rejects, that is passed immediately.
|
|
*/
|
|
Q.delay = function Q_delay(object, timeout) {
|
|
if (timeout === void 0) {
|
|
timeout = object;
|
|
object = void 0;
|
|
}
|
|
return Q(object).delay(timeout);
|
|
};
|
|
|
|
/**
|
|
* Causes a promise to be rejected if it does not get fulfilled before
|
|
* some milliseconds time out.
|
|
* @param {Any*} promise
|
|
* @param {Number} milliseconds timeout
|
|
* @param {String} custom error message (optional)
|
|
* @returns a promise for the resolution of the given promise if it is
|
|
* fulfilled before the timeout, otherwise rejected.
|
|
*/
|
|
Q.timeout = function Q_timeout(object, ms, message) {
|
|
return Q(object).timeout(ms, message);
|
|
};
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
Q.spread = Q_spread;
|
|
function Q_spread(value, fulfilled, rejected) {
|
|
return Q(value).spread(fulfilled, rejected);
|
|
}
|
|
|
|
/**
|
|
* If two promises eventually fulfill to the same value, promises that value,
|
|
* but otherwise rejects.
|
|
* @param x {Any*}
|
|
* @param y {Any*}
|
|
* @returns {Any*} a promise for x and y if they are the same, but a rejection
|
|
* otherwise.
|
|
*
|
|
*/
|
|
Q.join = function Q_join(x, y) {
|
|
return Q.spread([x, y], function Q_joined(x, y) {
|
|
if (x === y) {
|
|
// TODO: "===" should be Object.is or equiv
|
|
return x;
|
|
} else {
|
|
throw new Error("Can't join: not the same: " + x + " " + y);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Returns a promise for the first of an array of promises to become fulfilled.
|
|
* @param answers {Array} promises to race
|
|
* @returns {Promise} the first promise to be fulfilled
|
|
*/
|
|
Q.race = Q_race;
|
|
function Q_race(answerPs) {
|
|
return new Promise(function(deferred) {
|
|
answerPs.forEach(function(answerP) {
|
|
Q(answerP).then(deferred.resolve, deferred.reject);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Calls the promised function in a future turn.
|
|
* @param object promise or immediate reference for target function
|
|
* @param ...args array of application arguments
|
|
*/
|
|
Q.try = function Q_try(callback) {
|
|
return Q(callback).dispatch("call", [[]]);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Q.function = Promise_function;
|
|
function Promise_function(wrapped) {
|
|
return function promiseFunctionWrapper() {
|
|
var args = new Array(arguments.length);
|
|
for (var index = 0; index < arguments.length; index++) {
|
|
args[index] = arguments[index];
|
|
}
|
|
return Q(wrapped).apply(this, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The promised function decorator ensures that any promise arguments
|
|
* are settled and passed as values (`this` is also settled and passed
|
|
* as a value). It will also ensure that the result of a function is
|
|
* always a promise.
|
|
*
|
|
* @example
|
|
* var add = Q.promised(function (a, b) {
|
|
* return a + b;
|
|
* });
|
|
* add(Q(a), Q(B));
|
|
*
|
|
* @param {function} callback The function to decorate
|
|
* @returns {function} a function that has been decorated.
|
|
*/
|
|
Q.promised = function Q_promised(callback) {
|
|
return function promisedMethod() {
|
|
var args = new Array(arguments.length);
|
|
for (var index = 0; index < arguments.length; index++) {
|
|
args[index] = arguments[index];
|
|
}
|
|
return Q_spread(
|
|
[this, Q_all(args)],
|
|
function Q_promised_spread(self, args) {
|
|
return callback.apply(self, args);
|
|
}
|
|
);
|
|
};
|
|
};
|
|
|
|
/**
|
|
*/
|
|
Q.passByCopy = // TODO XXX experimental
|
|
Q.push = function (value) {
|
|
if (Object(value) === value && !Q_isPromise(value)) {
|
|
passByCopies.set(value, true);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
Q.isPortable = function (value) {
|
|
return Object(value) === value && passByCopies.has(value);
|
|
};
|
|
|
|
var passByCopies = new WeakMap();
|
|
|
|
/**
|
|
* The async function is a decorator for generator functions, turning
|
|
* them into asynchronous generators. Although generators are only
|
|
* part of the newest ECMAScript 6 drafts, 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.
|
|
*
|
|
* ES6 generators are currently part of V8 version 3.19 with the
|
|
* `--harmony-generators` runtime flag enabled. This function does not
|
|
* support the former, Pythonic generators that were only implemented
|
|
* by SpiderMonkey.
|
|
*
|
|
* 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.
|
|
*/
|
|
Q.async = Q_async;
|
|
function Q_async(makeGenerator) {
|
|
return function spawn() {
|
|
// when verb is "send", arg is a value
|
|
// when verb is "throw", arg is an exception
|
|
function continuer(verb, arg) {
|
|
var iteration;
|
|
try {
|
|
iteration = generator[verb](arg);
|
|
} catch (exception) {
|
|
return Q_reject(exception);
|
|
}
|
|
if (iteration.done) {
|
|
return Q(iteration.value);
|
|
} else {
|
|
return Q(iteration.value).then(callback, errback);
|
|
}
|
|
}
|
|
var generator = makeGenerator.apply(this, arguments);
|
|
var callback = continuer.bind(continuer, "next");
|
|
var errback = continuer.bind(continuer, "throw");
|
|
return callback();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The spawn function is a small wrapper around async that immediately
|
|
* calls the generator and also ends the promise chain, so that any
|
|
* unhandled errors are thrown instead of forwarded to the error
|
|
* handler. This is useful because it's extremely common to run
|
|
* generators at the top-level to work with libraries.
|
|
*/
|
|
Q.spawn = Q_spawn;
|
|
function Q_spawn(makeGenerator) {
|
|
Q_async(makeGenerator)().done();
|
|
}
|
|
|
|
|
|
// Thus begins the section dedicated to the Promise
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Q.Promise = Promise;
|
|
function Promise(handler) {
|
|
if (!(this instanceof Promise)) {
|
|
return new Promise(handler);
|
|
}
|
|
if (typeof handler === "function") {
|
|
var setup = handler;
|
|
var deferred = defer();
|
|
handler = Q_getHandler(deferred.promise);
|
|
try {
|
|
setup(deferred.resolve, deferred.reject, deferred.setEstimate);
|
|
} catch (error) {
|
|
deferred.reject(error);
|
|
}
|
|
}
|
|
handlers.set(this, handler);
|
|
}
|
|
|
|
/**
|
|
* 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.<Promise>} an array (or promise for an array) of values (or
|
|
* promises for values)
|
|
* @returns {Promise.<Array>} a promise for an array of the corresponding values
|
|
*/
|
|
Promise.all = Q_all;
|
|
|
|
/**
|
|
* Returns a promise for the first of an array of promises to become fulfilled.
|
|
* @param answers {Array} promises to race
|
|
* @returns {Promise} the first promise to be fulfilled
|
|
*/
|
|
Promise.race = Q_race;
|
|
|
|
/**
|
|
* Coerces a value to a promise. If the value is a promise, pass it through
|
|
* unaltered. If the value has a `then` method, it is presumed to be a promise
|
|
* but not one of our own, so it is treated as a “thenable” promise and this
|
|
* returns a promise that stands for it. Otherwise, this returns a promise that
|
|
* has already been fulfilled with the value.
|
|
* @param value promise, object with a then method, or a fulfillment value
|
|
* @returns {Promise} the same promise as given, or a promise for the given
|
|
* value
|
|
*/
|
|
Promise.resolve = Promise_resolve;
|
|
function Promise_resolve(value) {
|
|
return Q(value);
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that has been rejected with a reason, which should be an
|
|
* instance of `Error`.
|
|
* @param reason value describing the failure
|
|
* @returns {Promise} rejection
|
|
*/
|
|
Promise.reject = Q_reject;
|
|
|
|
/**
|
|
* @returns {boolean} whether the given value is a promise.
|
|
*/
|
|
Q.isPromise = Q_isPromise;
|
|
function Q_isPromise(object) {
|
|
return isObject(object) && !!handlers.get(object);
|
|
}
|
|
|
|
/**
|
|
* @returns {boolean} whether the given value is an object with a then method.
|
|
* @private
|
|
*/
|
|
function isThenable(object) {
|
|
return isObject(object) && typeof object.then === "function";
|
|
}
|
|
|
|
/**
|
|
* Synchronously produces a snapshot of the internal state of the promise. The
|
|
* object will have a `state` property. If the `state` is `"pending"`, there
|
|
* will be no further information. If the `state` is `"fulfilled"`, there will
|
|
* be a `value` property. If the state is `"rejected"` there will be a `reason`
|
|
* property. If the promise was constructed from a “thenable” and `then` nor
|
|
* any other method has been dispatched on the promise has been called, the
|
|
* state will be `"pending"`. The state object will not be updated if the
|
|
* state changes and changing it will have no effect on the promise. Every
|
|
* call to `inspect` produces a unique object.
|
|
* @returns {{state: string, value?, reason?}}
|
|
*/
|
|
Promise.prototype.inspect = function Promise_inspect() {
|
|
// the second layer captures only the relevant "state" properties of the
|
|
// handler to prevent leaking the capability to access or alter the
|
|
// handler.
|
|
return Q_getHandler(this).inspect();
|
|
};
|
|
|
|
/**
|
|
* @returns {boolean} whether the promise is waiting for a result.
|
|
*/
|
|
Promise.prototype.isPending = function Promise_isPending() {
|
|
return Q_getHandler(this).state === "pending";
|
|
};
|
|
|
|
/**
|
|
* @returns {boolean} whether the promise has ended in a result and has a
|
|
* fulfillment value.
|
|
*/
|
|
Promise.prototype.isFulfilled = function Promise_isFulfilled() {
|
|
return Q_getHandler(this).state === "fulfilled";
|
|
};
|
|
|
|
/**
|
|
* @returns {boolean} whether the promise has ended poorly and has a reason for
|
|
* its rejection.
|
|
*/
|
|
Promise.prototype.isRejected = function Promise_isRejected() {
|
|
return Q_getHandler(this).state === "rejected";
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.toBePassed = function Promise_toBePassed() {
|
|
return Q_getHandler(this).state === "passed";
|
|
};
|
|
|
|
/**
|
|
* @returns {string} merely `"[object Promise]"`
|
|
*/
|
|
Promise.prototype.toString = function Promise_toString() {
|
|
return "[object Promise]";
|
|
};
|
|
|
|
/**
|
|
* Creates a new promise, waits for this promise to be resolved, and informs
|
|
* either the fullfilled or rejected handler of the result. Whatever result
|
|
* comes of the fulfilled or rejected handler, a value returned, a promise
|
|
* returned, or an error thrown, becomes the resolution for the promise
|
|
* returned by `then`.
|
|
*
|
|
* @param fulfilled
|
|
* @param rejected
|
|
* @returns {Promise} for the result of `fulfilled` or `rejected`.
|
|
*/
|
|
Promise.prototype.then = function Promise_then(fulfilled, rejected, ms) {
|
|
var self = this;
|
|
var deferred = defer();
|
|
|
|
var _fulfilled;
|
|
if (typeof fulfilled === "function") {
|
|
_fulfilled = function Promise_then_fulfilled(value) {
|
|
try {
|
|
deferred.resolve(fulfilled.call(void 0, value));
|
|
} catch (error) {
|
|
deferred.reject(error);
|
|
}
|
|
};
|
|
} else {
|
|
_fulfilled = deferred.resolve;
|
|
}
|
|
|
|
var _rejected;
|
|
if (typeof rejected === "function") {
|
|
_rejected = function Promise_then_rejected(error) {
|
|
try {
|
|
deferred.resolve(rejected.call(void 0, error));
|
|
} catch (newError) {
|
|
deferred.reject(newError);
|
|
}
|
|
};
|
|
} else {
|
|
_rejected = deferred.reject;
|
|
}
|
|
|
|
this.done(_fulfilled, _rejected);
|
|
|
|
if (ms !== void 0) {
|
|
var updateEstimate = function Promise_then_updateEstimate() {
|
|
deferred.setEstimate(self.getEstimate() + ms);
|
|
};
|
|
this.observeEstimate(updateEstimate);
|
|
updateEstimate();
|
|
}
|
|
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Terminates a chain of promises, forcing rejections to be
|
|
* thrown as exceptions.
|
|
* @param fulfilled
|
|
* @param rejected
|
|
*/
|
|
Promise.prototype.done = function Promise_done(fulfilled, rejected) {
|
|
var self = this;
|
|
var done = false; // ensure the untrusted promise makes at most a
|
|
// single call to one of the callbacks
|
|
asap(function Promise_done_task() {
|
|
var _fulfilled;
|
|
if (typeof fulfilled === "function") {
|
|
if (Q.onerror) {
|
|
_fulfilled = function Promise_done_fulfilled(value) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
try {
|
|
fulfilled.call(void 0, value);
|
|
} catch (error) {
|
|
// fallback to rethrow is still necessary because
|
|
// _fulfilled is not called in the same event as the
|
|
// above guard.
|
|
(Q.onerror || Promise_rethrow)(error);
|
|
}
|
|
};
|
|
} else {
|
|
_fulfilled = function Promise_done_fulfilled(value) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
fulfilled.call(void 0, value);
|
|
};
|
|
}
|
|
}
|
|
|
|
var _rejected;
|
|
if (typeof rejected === "function" && Q.onerror) {
|
|
_rejected = function Promise_done_rejected(error) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
makeStackTraceLong(error, self);
|
|
try {
|
|
rejected.call(void 0, error);
|
|
} catch (newError) {
|
|
(Q.onerror || Promise_rethrow)(newError);
|
|
}
|
|
};
|
|
} else if (typeof rejected === "function") {
|
|
_rejected = function Promise_done_rejected(error) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
makeStackTraceLong(error, self);
|
|
rejected.call(void 0, error);
|
|
};
|
|
} else {
|
|
_rejected = Q.onerror || Promise_rethrow;
|
|
}
|
|
|
|
if (typeof process === "object" && process.domain) {
|
|
_rejected = process.domain.bind(_rejected);
|
|
}
|
|
|
|
Q_getHandler(self).dispatch(_fulfilled, "then", [_rejected]);
|
|
});
|
|
};
|
|
|
|
function Promise_rethrow(error) {
|
|
throw error;
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.thenResolve = function Promise_thenResolve(value) {
|
|
// Wrapping ahead of time to forestall multiple wrappers.
|
|
value = Q(value);
|
|
// Using all is necessary to aggregate the estimated time to completion.
|
|
return Q_all([this, value]).then(function Promise_thenResolve_resolved() {
|
|
return value;
|
|
}, null, 0);
|
|
// 0: does not contribute significantly to the estimated time to
|
|
// completion.
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.thenReject = function Promise_thenReject(error) {
|
|
return this.then(function Promise_thenReject_resolved() {
|
|
throw error;
|
|
}, null, 0);
|
|
// 0: does not contribute significantly to the estimated time to
|
|
// completion.
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.all = function Promise_all() {
|
|
return this.then(Q_all);
|
|
};
|
|
|
|
/**
|
|
* Turns an array of promises into a promise for an array of their states (as
|
|
* returned by `inspect`) when they have all settled.
|
|
* @param {Array[Any*]} values an array (or promise for an array) of values (or
|
|
* promises for values)
|
|
* @returns {Array[State]} an array of states for the respective values.
|
|
*/
|
|
Promise.prototype.allSettled = function Promise_allSettled() {
|
|
return this.then(Q_allSettled);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.catch = function Promise_catch(rejected) {
|
|
return this.then(void 0, rejected);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.finally = function Promise_finally(callback, ms) {
|
|
if (!callback) {
|
|
return this;
|
|
}
|
|
callback = Q(callback);
|
|
return this.then(function (value) {
|
|
return callback.call().then(function Promise_finally_fulfilled() {
|
|
return value;
|
|
});
|
|
}, function (reason) {
|
|
// TODO attempt to recycle the rejection with "this".
|
|
return callback.call().then(function Promise_finally_rejected() {
|
|
throw reason;
|
|
});
|
|
}, ms);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.observeEstimate = function Promise_observeEstimate(emit) {
|
|
this.rawDispatch(null, "estimate", [emit]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.getEstimate = function Promise_getEstimate() {
|
|
return Q_getHandler(this).estimate;
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.dispatch = function Promise_dispatch(op, args) {
|
|
var deferred = defer();
|
|
this.rawDispatch(deferred.resolve, op, args);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
*/
|
|
Promise.prototype.rawDispatch = function Promise_rawDispatch(resolve, op, args) {
|
|
var self = this;
|
|
asap(function Promise_dispatch_task() {
|
|
Q_getHandler(self).dispatch(resolve, op, args);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.get = function Promise_get(name) {
|
|
return this.dispatch("get", [name]);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.invoke = function Promise_invoke(name /*...args*/) {
|
|
var args = new Array(arguments.length - 1);
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
args[index - 1] = arguments[index];
|
|
}
|
|
return this.dispatch("invoke", [name, args]);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.apply = function Promise_apply(thisp, args) {
|
|
return this.dispatch("call", [args, thisp]);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.call = function Promise_call(thisp /*, ...args*/) {
|
|
var args = new Array(Math.max(0, arguments.length - 1));
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
args[index - 1] = arguments[index];
|
|
}
|
|
return this.dispatch("call", [args, thisp]);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.bind = function Promise_bind(thisp /*, ...args*/) {
|
|
var self = this;
|
|
var args = new Array(Math.max(0, arguments.length - 1));
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
args[index - 1] = arguments[index];
|
|
}
|
|
return function Promise_bind_bound(/*...args*/) {
|
|
var boundArgs = args.slice();
|
|
for (var index = 0; index < arguments.length; index++) {
|
|
boundArgs[boundArgs.length] = arguments[index];
|
|
}
|
|
return self.dispatch("call", [boundArgs, thisp]);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.keys = function Promise_keys() {
|
|
return this.dispatch("keys", []);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.iterate = function Promise_iterate() {
|
|
return this.dispatch("iterate", []);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.spread = function Promise_spread(fulfilled, rejected, ms) {
|
|
return this.all().then(function Promise_spread_fulfilled(array) {
|
|
return fulfilled.apply(void 0, array);
|
|
}, rejected, ms);
|
|
};
|
|
|
|
/**
|
|
* Causes a promise to be rejected if it does not get fulfilled before
|
|
* some milliseconds time out.
|
|
* @param {Number} milliseconds timeout
|
|
* @param {String} custom error message (optional)
|
|
* @returns a promise for the resolution of the given promise if it is
|
|
* fulfilled before the timeout, otherwise rejected.
|
|
*/
|
|
Promise.prototype.timeout = function Promsie_timeout(ms, message) {
|
|
var deferred = defer();
|
|
var timeoutId = setTimeout(function Promise_timeout_task() {
|
|
deferred.reject(new Error(message || "Timed out after " + ms + " ms"));
|
|
}, ms);
|
|
|
|
this.then(function Promise_timeout_fulfilled(value) {
|
|
clearTimeout(timeoutId);
|
|
deferred.resolve(value);
|
|
}, function Promise_timeout_rejected(error) {
|
|
clearTimeout(timeoutId);
|
|
deferred.reject(error);
|
|
});
|
|
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Returns a promise for the given value (or promised value), some
|
|
* milliseconds after it resolved. Passes rejections immediately.
|
|
* @param {Any*} promise
|
|
* @param {Number} milliseconds
|
|
* @returns a promise for the resolution of the given promise after milliseconds
|
|
* time has elapsed since the resolution of the given promise.
|
|
* If the given promise rejects, that is passed immediately.
|
|
*/
|
|
Promise.prototype.delay = function Promise_delay(ms) {
|
|
return this.then(function Promise_delay_fulfilled(value) {
|
|
var deferred = defer();
|
|
deferred.setEstimate(Date.now() + ms);
|
|
setTimeout(function Promise_delay_task() {
|
|
deferred.resolve(value);
|
|
}, ms);
|
|
return deferred.promise;
|
|
}, null, ms);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.pull = function Promise_pull() {
|
|
return this.dispatch("pull", []);
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.pass = function Promise_pass() {
|
|
if (!this.toBePassed()) {
|
|
return new Promise(new Passed(this));
|
|
} else {
|
|
return this;
|
|
}
|
|
};
|
|
|
|
|
|
// Thus begins the portion dedicated to the deferred
|
|
|
|
var promises = new WeakMap();
|
|
|
|
function Deferred(promise) {
|
|
this.promise = promise;
|
|
// A deferred has an intrinsic promise, denoted by its hidden handler
|
|
// property. The promise property of the deferred may be assigned to a
|
|
// different promise (as it is in a Queue), but the intrinsic promise does
|
|
// not change.
|
|
promises.set(this, promise);
|
|
var self = this;
|
|
var resolve = this.resolve;
|
|
this.resolve = function (value) {
|
|
resolve.call(self, value);
|
|
};
|
|
var reject = this.reject;
|
|
this.reject = function (error) {
|
|
reject.call(self, error);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Deferred.prototype.resolve = function Deferred_resolve(value) {
|
|
var handler = Q_getHandler(promises.get(this));
|
|
if (!handler.messages) {
|
|
return;
|
|
}
|
|
handler.become(Q(value));
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Deferred.prototype.reject = function Deferred_reject(reason) {
|
|
var handler = Q_getHandler(promises.get(this));
|
|
if (!handler.messages) {
|
|
return;
|
|
}
|
|
handler.become(Q_reject(reason));
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Deferred.prototype.setEstimate = function Deferred_setEstimate(estimate) {
|
|
estimate = +estimate;
|
|
if (estimate !== estimate) {
|
|
estimate = Infinity;
|
|
}
|
|
if (estimate < 1e12 && estimate !== -Infinity) {
|
|
throw new Error("Estimate values should be a number of miliseconds in the future");
|
|
}
|
|
var handler = Q_getHandler(promises.get(this));
|
|
// TODO There is a bit of capability leakage going on here. The Deferred
|
|
// should only be able to set the estimate for its original
|
|
// Pending, not for any handler that promise subsequently became.
|
|
if (handler.setEstimate) {
|
|
handler.setEstimate(estimate);
|
|
}
|
|
};
|
|
|
|
// Thus ends the public interface
|
|
|
|
// Thus begins the portion dedicated to handlers
|
|
|
|
function Fulfilled(value) {
|
|
this.value = value;
|
|
this.estimate = Date.now();
|
|
}
|
|
|
|
Fulfilled.prototype.state = "fulfilled";
|
|
|
|
Fulfilled.prototype.inspect = function Fulfilled_inspect() {
|
|
return {state: "fulfilled", value: this.value};
|
|
};
|
|
|
|
Fulfilled.prototype.dispatch = function Fulfilled_dispatch(
|
|
resolve, op, operands
|
|
) {
|
|
var result;
|
|
if (
|
|
op === "then" ||
|
|
op === "get" ||
|
|
op === "call" ||
|
|
op === "invoke" ||
|
|
op === "keys" ||
|
|
op === "iterate" ||
|
|
op === "pull"
|
|
) {
|
|
try {
|
|
result = this[op].apply(this, operands);
|
|
} catch (exception) {
|
|
result = Q_reject(exception);
|
|
}
|
|
} else if (op === "estimate") {
|
|
operands[0].call(void 0, this.estimate);
|
|
} else {
|
|
var error = new Error(
|
|
"Fulfilled promises do not support the " + op + " operator"
|
|
);
|
|
result = Q_reject(error);
|
|
}
|
|
if (resolve) {
|
|
resolve(result);
|
|
}
|
|
};
|
|
|
|
Fulfilled.prototype.then = function Fulfilled_then() {
|
|
return this.value;
|
|
};
|
|
|
|
Fulfilled.prototype.get = function Fulfilled_get(name) {
|
|
return this.value[name];
|
|
};
|
|
|
|
Fulfilled.prototype.call = function Fulfilled_call(args, thisp) {
|
|
return this.callInvoke(this.value, args, thisp);
|
|
};
|
|
|
|
Fulfilled.prototype.invoke = function Fulfilled_invoke(name, args) {
|
|
return this.callInvoke(this.value[name], args, this.value);
|
|
};
|
|
|
|
Fulfilled.prototype.callInvoke = function Fulfilled_callInvoke(callback, args, thisp) {
|
|
var waitToBePassed;
|
|
for (var index = 0; index < args.length; index++) {
|
|
if (Q_isPromise(args[index]) && args[index].toBePassed()) {
|
|
waitToBePassed = waitToBePassed || [];
|
|
waitToBePassed.push(args[index]);
|
|
}
|
|
}
|
|
if (waitToBePassed) {
|
|
var self = this;
|
|
return Q_all(waitToBePassed).then(function () {
|
|
return self.callInvoke(callback, args.map(function (arg) {
|
|
if (Q_isPromise(arg) && arg.toBePassed()) {
|
|
return arg.inspect().value;
|
|
} else {
|
|
return arg;
|
|
}
|
|
}), thisp);
|
|
});
|
|
} else {
|
|
return callback.apply(thisp, args);
|
|
}
|
|
};
|
|
|
|
Fulfilled.prototype.keys = function Fulfilled_keys() {
|
|
return Object.keys(this.value);
|
|
};
|
|
|
|
Fulfilled.prototype.iterate = function Fulfilled_iterate() {
|
|
return iterate(this.value);
|
|
};
|
|
|
|
Fulfilled.prototype.pull = function Fulfilled_pull() {
|
|
var result;
|
|
if (Object(this.value) === this.value) {
|
|
result = Array.isArray(this.value) ? [] : {};
|
|
for (var name in this.value) {
|
|
result[name] = this.value[name];
|
|
}
|
|
} else {
|
|
result = this.value;
|
|
}
|
|
return Q.push(result);
|
|
};
|
|
|
|
|
|
function Rejected(reason) {
|
|
this.reason = reason;
|
|
this.estimate = Infinity;
|
|
}
|
|
|
|
Rejected.prototype.state = "rejected";
|
|
|
|
Rejected.prototype.inspect = function Rejected_inspect() {
|
|
return {state: "rejected", reason: this.reason};
|
|
};
|
|
|
|
Rejected.prototype.dispatch = function Rejected_dispatch(
|
|
resolve, op, operands
|
|
) {
|
|
var result;
|
|
if (op === "then") {
|
|
result = this.then(resolve, operands[0]);
|
|
} else {
|
|
result = this;
|
|
}
|
|
if (resolve) {
|
|
resolve(result);
|
|
}
|
|
};
|
|
|
|
Rejected.prototype.then = function Rejected_then(
|
|
resolve, rejected
|
|
) {
|
|
return rejected ? rejected(this.reason) : this;
|
|
};
|
|
|
|
|
|
function Pending() {
|
|
// if "messages" 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 messages array is itself an array of complete arguments to
|
|
// forward to the resolved promise. We coerce the resolution value to a
|
|
// promise using the `resolve` function because it handles both fully
|
|
// non-thenable values and other thenables gracefully.
|
|
this.messages = [];
|
|
this.observers = [];
|
|
this.estimate = Infinity;
|
|
}
|
|
|
|
Pending.prototype.state = "pending";
|
|
|
|
Pending.prototype.inspect = function Pending_inspect() {
|
|
return {state: "pending"};
|
|
};
|
|
|
|
Pending.prototype.dispatch = function Pending_dispatch(resolve, op, operands) {
|
|
this.messages.push([resolve, op, operands]);
|
|
if (op === "estimate") {
|
|
this.observers.push(operands[0]);
|
|
var self = this;
|
|
asap(function Pending_dispatch_task() {
|
|
operands[0].call(void 0, self.estimate);
|
|
});
|
|
}
|
|
};
|
|
|
|
Pending.prototype.become = function Pending_become(promise) {
|
|
this.became = theViciousCycle;
|
|
var handler = Q_getHandler(promise);
|
|
this.became = handler;
|
|
|
|
handlers.set(promise, handler);
|
|
this.promise = void 0;
|
|
|
|
this.messages.forEach(function Pending_become_eachMessage(message) {
|
|
// makeQ does not have this asap call, so it must be queueing events
|
|
// downstream. TODO look at makeQ to ascertain
|
|
asap(function Pending_become_eachMessage_task() {
|
|
var handler = Q_getHandler(promise);
|
|
handler.dispatch.apply(handler, message);
|
|
});
|
|
});
|
|
|
|
this.messages = void 0;
|
|
this.observers = void 0;
|
|
};
|
|
|
|
Pending.prototype.setEstimate = function Pending_setEstimate(estimate) {
|
|
if (this.observers) {
|
|
var self = this;
|
|
self.estimate = estimate;
|
|
this.observers.forEach(function Pending_eachObserver(observer) {
|
|
asap(function Pending_setEstimate_eachObserver_task() {
|
|
observer.call(void 0, estimate);
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
function Thenable(thenable) {
|
|
this.thenable = thenable;
|
|
this.became = null;
|
|
this.estimate = Infinity;
|
|
}
|
|
|
|
Thenable.prototype.state = "thenable";
|
|
|
|
Thenable.prototype.inspect = function Thenable_inspect() {
|
|
return {state: "pending"};
|
|
};
|
|
|
|
Thenable.prototype.cast = function Thenable_cast() {
|
|
if (!this.became) {
|
|
var deferred = defer();
|
|
var thenable = this.thenable;
|
|
asap(function Thenable_cast_task() {
|
|
try {
|
|
thenable.then(deferred.resolve, deferred.reject);
|
|
} catch (exception) {
|
|
deferred.reject(exception);
|
|
}
|
|
});
|
|
this.became = Q_getHandler(deferred.promise);
|
|
}
|
|
return this.became;
|
|
};
|
|
|
|
Thenable.prototype.dispatch = function Thenable_dispatch(resolve, op, args) {
|
|
this.cast().dispatch(resolve, op, args);
|
|
};
|
|
|
|
|
|
function Passed(promise) {
|
|
this.promise = promise;
|
|
}
|
|
|
|
Passed.prototype.state = "passed";
|
|
|
|
Passed.prototype.inspect = function Passed_inspect() {
|
|
return this.promise.inspect();
|
|
};
|
|
|
|
Passed.prototype.dispatch = function Passed_dispatch(resolve, op, args) {
|
|
return this.promise.rawDispatch(resolve, op, args);
|
|
};
|
|
|
|
|
|
// Thus begins the Q Node.js bridge
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
Q.ninvoke = function Q_ninvoke(object, name /*...args*/) {
|
|
var args = new Array(Math.max(0, arguments.length - 1));
|
|
for (var index = 2; index < arguments.length; index++) {
|
|
args[index - 2] = arguments[index];
|
|
}
|
|
var deferred = Q.defer();
|
|
args[index - 2] = deferred.makeNodeResolver();
|
|
Q(object).dispatch("invoke", [name, args]).catch(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
Promise.prototype.ninvoke = function Promise_ninvoke(name /*...args*/) {
|
|
var args = new Array(arguments.length);
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
args[index - 1] = arguments[index];
|
|
}
|
|
var deferred = Q.defer();
|
|
args[index - 1] = deferred.makeNodeResolver();
|
|
this.dispatch("invoke", [name, args]).catch(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Wraps a Node.js continuation passing function and returns an equivalent
|
|
* version that returns a promise.
|
|
* @example
|
|
* Q.denodeify(FS.readFile)(__filename, "utf-8")
|
|
* .then(console.log)
|
|
* .done()
|
|
*/
|
|
Q.denodeify = function Q_denodeify(callback, pattern) {
|
|
return function denodeified() {
|
|
var args = new Array(arguments.length + 1);
|
|
var index = 0;
|
|
for (; index < arguments.length; index++) {
|
|
args[index] = arguments[index];
|
|
}
|
|
var deferred = Q.defer();
|
|
args[index] = deferred.makeNodeResolver(pattern);
|
|
Q(callback).apply(this, args).catch(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Creates a Node.js-style callback that will resolve or reject the deferred
|
|
* promise.
|
|
* @param unpack `true` means that the Node.js-style-callback accepts a
|
|
* fixed or variable number of arguments and that the deferred should be resolved
|
|
* with an array of these value arguments, or rejected with the error argument.
|
|
* An array of names means that the Node.js-style-callback accepts a fixed
|
|
* number of arguments, and that the resolution should be an object with
|
|
* properties corresponding to the given names and respective value arguments.
|
|
* @returns a nodeback
|
|
*/
|
|
Deferred.prototype.makeNodeResolver = function (unpack) {
|
|
var resolve = this.resolve;
|
|
if (unpack === true) {
|
|
return function variadicNodebackToResolver(error) {
|
|
if (error) {
|
|
resolve(Q_reject(error));
|
|
} else {
|
|
var value = new Array(Math.max(0, arguments.length - 1));
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
value[index - 1] = arguments[index];
|
|
}
|
|
resolve(value);
|
|
}
|
|
};
|
|
} else if (unpack) {
|
|
return function namedArgumentNodebackToResolver(error) {
|
|
if (error) {
|
|
resolve(Q_reject(error));
|
|
} else {
|
|
var value = {};
|
|
for (var index = 0; index < unpack.length; index++) {
|
|
value[unpack[index]] = arguments[index + 1];
|
|
}
|
|
resolve(value);
|
|
}
|
|
};
|
|
} else {
|
|
return function nodebackToResolver(error, value) {
|
|
if (error) {
|
|
resolve(Q_reject(error));
|
|
} else {
|
|
resolve(value);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* TODO
|
|
*/
|
|
Promise.prototype.nodeify = function Promise_nodeify(nodeback) {
|
|
if (nodeback) {
|
|
this.done(function (value) {
|
|
nodeback(null, value);
|
|
}, nodeback);
|
|
} else {
|
|
return this;
|
|
}
|
|
};
|
|
|
|
|
|
// DEPRECATED
|
|
|
|
Q.nextTick = deprecate(asap, "nextTick", "asap package");
|
|
|
|
Q.resolve = deprecate(Q, "resolve", "Q");
|
|
|
|
Q.fulfill = deprecate(Q, "fulfill", "Q");
|
|
|
|
Q.isPromiseAlike = deprecate(isThenable, "isPromiseAlike", "(not supported)");
|
|
|
|
Q.fail = deprecate(function (value, rejected) {
|
|
return Q(value).catch(rejected);
|
|
}, "Q.fail", "Q(value).catch");
|
|
|
|
Q.fin = deprecate(function (value, regardless) {
|
|
return Q(value).finally(regardless);
|
|
}, "Q.fin", "Q(value).finally");
|
|
|
|
Q.progress = deprecate(function (value) {
|
|
return value;
|
|
}, "Q.progress", "no longer supported");
|
|
|
|
Q.thenResolve = deprecate(function (promise, value) {
|
|
return Q(promise).thenResolve(value);
|
|
}, "thenResolve", "Q(value).thenResolve");
|
|
|
|
Q.thenReject = deprecate(function (promise, reason) {
|
|
return Q(promise).thenResolve(reason);
|
|
}, "thenResolve", "Q(value).thenResolve");
|
|
|
|
Q.isPending = deprecate(function (value) {
|
|
return Q(value).isPending();
|
|
}, "isPending", "Q(value).isPending");
|
|
|
|
Q.isFulfilled = deprecate(function (value) {
|
|
return Q(value).isFulfilled();
|
|
}, "isFulfilled", "Q(value).isFulfilled");
|
|
|
|
Q.isRejected = deprecate(function (value) {
|
|
return Q(value).isRejected();
|
|
}, "isRejected", "Q(value).isRejected");
|
|
|
|
Q.master = deprecate(function (value) {
|
|
return value;
|
|
}, "master", "no longer necessary");
|
|
|
|
Q.makePromise = function () {
|
|
throw new Error("makePromise is no longer supported");
|
|
};
|
|
|
|
Q.dispatch = deprecate(function (value, op, operands) {
|
|
return Q(value).dispatch(op, operands);
|
|
}, "dispatch", "Q(value).dispatch");
|
|
|
|
Q.get = deprecate(function (object, name) {
|
|
return Q(object).get(name);
|
|
}, "get", "Q(value).get");
|
|
|
|
Q.keys = deprecate(function (object) {
|
|
return Q(object).keys();
|
|
}, "keys", "Q(value).keys");
|
|
|
|
Q.post = deprecate(function (object, name, args) {
|
|
return Q(object).post(name, args);
|
|
}, "post", "Q(value).invoke (spread arguments)");
|
|
|
|
Q.mapply = deprecate(function (object, name, args) {
|
|
return Q(object).post(name, args);
|
|
}, "post", "Q(value).invoke (spread arguments)");
|
|
|
|
Q.send = deprecate(function (object, name) {
|
|
return Q(object).post(name, Array.prototype.slice.call(arguments, 2));
|
|
}, "send", "Q(value).invoke");
|
|
|
|
Q.set = function () {
|
|
throw new Error("Q.set no longer supported");
|
|
};
|
|
|
|
Q.delete = function () {
|
|
throw new Error("Q.delete no longer supported");
|
|
};
|
|
|
|
Q.nearer = deprecate(function (value) {
|
|
if (Q_isPromise(value) && value.isFulfilled()) {
|
|
return value.inspect().value;
|
|
} else {
|
|
return value;
|
|
}
|
|
}, "nearer", "inspect().value (+nuances)");
|
|
|
|
Q.fapply = deprecate(function (callback, args) {
|
|
return Q(callback).dispatch("call", [args]);
|
|
}, "fapply", "Q(callback).apply(thisp, args)");
|
|
|
|
Q.fcall = deprecate(function (callback /*, ...args*/) {
|
|
return Q(callback).dispatch("call", [Array.prototype.slice.call(arguments, 1)]);
|
|
}, "fcall", "Q(callback).call(thisp, ...args)");
|
|
|
|
Q.fbind = deprecate(function (object /*...args*/) {
|
|
var promise = Q(object);
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
return function fbound() {
|
|
return promise.dispatch("call", [
|
|
args.concat(Array.prototype.slice.call(arguments)),
|
|
this
|
|
]);
|
|
};
|
|
}, "fbind", "bind with thisp");
|
|
|
|
Q.promise = deprecate(Promise, "promise", "Promise");
|
|
|
|
Promise.prototype.fapply = deprecate(function (args) {
|
|
return this.dispatch("call", [args]);
|
|
}, "fapply", "apply with thisp");
|
|
|
|
Promise.prototype.fcall = deprecate(function (/*...args*/) {
|
|
return this.dispatch("call", [Array.prototype.slice.call(arguments)]);
|
|
}, "fcall", "try or call with thisp");
|
|
|
|
Promise.prototype.fail = deprecate(function (rejected) {
|
|
return this.catch(rejected);
|
|
}, "fail", "catch");
|
|
|
|
Promise.prototype.fin = deprecate(function (regardless) {
|
|
return this.finally(regardless);
|
|
}, "fin", "finally");
|
|
|
|
Promise.prototype.set = function () {
|
|
throw new Error("Promise set no longer supported");
|
|
};
|
|
|
|
Promise.prototype.delete = function () {
|
|
throw new Error("Promise delete no longer supported");
|
|
};
|
|
|
|
Deferred.prototype.notify = deprecate(function () {
|
|
}, "notify", "no longer supported");
|
|
|
|
Promise.prototype.progress = deprecate(function () {
|
|
return this;
|
|
}, "progress", "no longer supported");
|
|
|
|
// alternative proposed by Redsandro, dropped in favor of post to streamline
|
|
// the interface
|
|
Promise.prototype.mapply = deprecate(function (name, args) {
|
|
return this.dispatch("invoke", [name, args]);
|
|
}, "mapply", "invoke");
|
|
|
|
Promise.prototype.fbind = deprecate(function () {
|
|
return Q.fbind.apply(Q, [void 0].concat(Array.prototype.slice.call(arguments)));
|
|
}, "fbind", "bind(thisp, ...args)");
|
|
|
|
// alternative proposed by Mark Miller, dropped in favor of invoke
|
|
Promise.prototype.send = deprecate(function () {
|
|
return this.dispatch("invoke", [name, Array.prototype.slice.call(arguments, 1)]);
|
|
}, "send", "invoke");
|
|
|
|
// alternative proposed by Redsandro, dropped in favor of invoke
|
|
Promise.prototype.mcall = deprecate(function () {
|
|
return this.dispatch("invoke", [name, Array.prototype.slice.call(arguments, 1)]);
|
|
}, "mcall", "invoke");
|
|
|
|
Promise.prototype.passByCopy = deprecate(function (value) {
|
|
return value;
|
|
}, "passByCopy", "Q.passByCopy");
|
|
|
|
// Deprecated Node.js bridge promise methods
|
|
|
|
Q.nfapply = deprecate(function (callback, args) {
|
|
var deferred = Q.defer();
|
|
var nodeArgs = Array.prototype.slice.call(args);
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
Q(callback).apply(this, nodeArgs).catch(deferred.reject);
|
|
return deferred.promise;
|
|
}, "nfapply");
|
|
|
|
Promise.prototype.nfapply = deprecate(function (args) {
|
|
return Q.nfapply(this, args);
|
|
}, "nfapply");
|
|
|
|
Q.nfcall = deprecate(function (callback /*...args*/) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
return Q.nfapply(callback, args);
|
|
}, "nfcall");
|
|
|
|
Promise.prototype.nfcall = deprecate(function () {
|
|
var args = new Array(arguments.length);
|
|
for (var index = 0; index < arguments.length; index++) {
|
|
args[index] = arguments[index];
|
|
}
|
|
return Q.nfapply(this, args);
|
|
}, "nfcall");
|
|
|
|
Q.nfbind = deprecate(function (callback /*...args*/) {
|
|
var baseArgs = Array.prototype.slice.call(arguments, 1);
|
|
return function () {
|
|
var nodeArgs = baseArgs.concat(Array.prototype.slice.call(arguments));
|
|
var deferred = Q.defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
Q(callback).apply(this, nodeArgs).catch(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
}, "nfbind", "denodeify (with caveats)");
|
|
|
|
Promise.prototype.nfbind = deprecate(function () {
|
|
var args = new Array(arguments.length);
|
|
for (var index = 0; index < arguments.length; index++) {
|
|
args[index] = arguments[index];
|
|
}
|
|
return Q.nfbind(this, args);
|
|
}, "nfbind", "denodeify (with caveats)");
|
|
|
|
Q.nbind = deprecate(function (callback, thisp /*...args*/) {
|
|
var baseArgs = Array.prototype.slice.call(arguments, 2);
|
|
return function () {
|
|
var nodeArgs = baseArgs.concat(Array.prototype.slice.call(arguments));
|
|
var deferred = Q.defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
function bound() {
|
|
return callback.apply(thisp, arguments);
|
|
}
|
|
Q(bound).apply(this, nodeArgs).catch(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
}, "nbind", "denodeify (with caveats)");
|
|
|
|
Q.npost = deprecate(function (object, name, nodeArgs) {
|
|
var deferred = Q.defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
Q(object).dispatch("invoke", [name, nodeArgs]).catch(deferred.reject);
|
|
return deferred.promise;
|
|
}, "npost", "ninvoke (with spread arguments)");
|
|
|
|
Promise.prototype.npost = deprecate(function (name, args) {
|
|
return Q.npost(this, name, args);
|
|
}, "npost", "Q.ninvoke (with caveats)");
|
|
|
|
Q.nmapply = deprecate(Q.nmapply, "nmapply", "q/node nmapply");
|
|
Promise.prototype.nmapply = deprecate(Promise.prototype.npost, "nmapply", "Q.nmapply");
|
|
|
|
Q.nsend = deprecate(Q.ninvoke, "nsend", "q/node ninvoke");
|
|
Q.nmcall = deprecate(Q.ninvoke, "nmcall", "q/node ninvoke");
|
|
Promise.prototype.nsend = deprecate(Promise.prototype.ninvoke, "nsend", "q/node ninvoke");
|
|
Promise.prototype.nmcall = deprecate(Promise.prototype.ninvoke, "nmcall", "q/node ninvoke");
|
|
|
|
// All code before this point will be filtered from stack traces.
|
|
var qEndingLine = captureLine();
|
|
|