/* 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.} an array (or promise for an array) of values (or * promises for values) * @returns {Promise.} 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.} an array (or promise for an array) of values (or * promises for values) * @returns {Promise.} 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();