Comparing version 1.0.1 to 2.0.0
"use strict"; | ||
var NQ = require("../node"); | ||
var Q = require("../q"); | ||
@@ -21,3 +22,3 @@ var fs = require("fs"); | ||
suite("A fs.readFile", function () { | ||
var denodeified = Q.denodeify(fs.readFile); | ||
var denodeified = NQ.denodeify(fs.readFile); | ||
@@ -31,7 +32,7 @@ set("iterations", 1000); | ||
bench("with Q.nfcall", function (done) { | ||
Q.nfcall(fs.readFile, __filename).then(done); | ||
bench("with NQ.nfcall", function (done) { | ||
NQ.nfcall(fs.readFile, __filename).then(done); | ||
}); | ||
bench("with a Q.denodeify'ed version", function (done) { | ||
bench("with a NQ.denodeify'ed version", function (done) { | ||
denodeified(__filename).then(done); | ||
@@ -42,3 +43,3 @@ }); | ||
var deferred = Q.defer(); | ||
fs.readFile(__filename, deferred.makeNodeResolver()); | ||
fs.readFile(__filename, NQ.makeNodeResolver(deferred.resolve)); | ||
deferred.promise.then(done); | ||
@@ -45,0 +46,0 @@ }); |
{ | ||
"name": "q", | ||
"version": "1.0.1", | ||
"version": "2.0.0", | ||
"description": "A library for promises (CommonJS/Promises/A,B,D)", | ||
@@ -26,6 +26,6 @@ "homepage": "https://github.com/kriskowal/q", | ||
], | ||
"bugs": { | ||
"mail": "kris@cixar.com", | ||
"url": "http://github.com/kriskowal/q/issues" | ||
}, | ||
"credits": [ | ||
"Mark Miller <erights@google.com>", | ||
"Tyler Close" | ||
], | ||
"license": { | ||
@@ -35,3 +35,2 @@ "type": "MIT", | ||
}, | ||
"main": "q.js", | ||
"repository": { | ||
@@ -41,26 +40,29 @@ "type": "git", | ||
}, | ||
"engines": { | ||
"node": ">=0.6.0", | ||
"teleport": ">=0.2.0" | ||
"main": "q.js", | ||
"dependencies": { | ||
"asap": "^1.0.0", | ||
"collections": "^2.0.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"jshint": "~2.1.9", | ||
"cover": "*", | ||
"jasmine-node": "1.11.0", | ||
"opener": "*", | ||
"promises-aplus-tests": "1.x", | ||
"grunt": "~0.4.1", | ||
"grunt-cli": "~0.1.9", | ||
"grunt-contrib-uglify": "~0.2.2", | ||
"matcha": "~0.2.0" | ||
"jshint": "^2.4.4", | ||
"jasminum": "^2.0.0", | ||
"opener": "^1.3.0", | ||
"promises-aplus-tests": "^1.0.2", | ||
"istanbul": "^0.2.4", | ||
"matcha": "^0.2.0", | ||
"grunt": "^0.4.1", | ||
"grunt-cli": "^0.1.9", | ||
"grunt-contrib-uglify": "^0.2.2", | ||
"grunt-contrib-clean": "^0.5.0", | ||
"grunt-global-wrap": "^1.1.0", | ||
"grunt-amd-wrap": "^1.0.0", | ||
"grunt-s3": "^0.2.0-alpha.2" | ||
}, | ||
"scripts": { | ||
"test": "jasmine-node spec && promises-aplus-tests spec/aplus-adapter", | ||
"test-browser": "opener spec/q-spec.html", | ||
"benchmark": "matcha", | ||
"lint": "jshint q.js", | ||
"cover": "cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html", | ||
"minify": "grunt", | ||
"prepublish": "grunt" | ||
"test": "npm run lint && jasminum test && promises-aplus-tests test/aplus-adapter", | ||
"test:phantom": "jasminum-phantom test", | ||
"cover": "istanbul cover test/index.js && istanbul report html && opener coverage/index.html", | ||
"release": "grunt release", | ||
"benchmark": "matcha" | ||
}, | ||
@@ -74,5 +76,5 @@ "overlay": { | ||
}, | ||
"directories": { | ||
"test": "./spec" | ||
"volo": { | ||
"url": "http://q-releases.s3-website-us-west-1.amazonaws.com/{version}/amd/q.js" | ||
} | ||
} |
2654
q.js
// vim:ts=4:sts=4:sw=4: | ||
/*! | ||
* | ||
* Copyright 2009-2012 Kris Kowal under the terms of the MIT | ||
* Copyright 2009-2013 Kris Kowal under the terms of the MIT | ||
* license found at http://github.com/kriskowal/q/raw/master/LICENSE | ||
@@ -28,38 +28,3 @@ * | ||
*/ | ||
(function (definition) { | ||
// Turn off strict mode for this function so we can assign to global.Q | ||
/* jshint strict: false */ | ||
// This file will function properly as a <script> tag, or a module | ||
// using CommonJS and NodeJS or RequireJS module formats. In | ||
// Common/Node/RequireJS, the module exports the Q API and when | ||
// executed as a simple <script>, it creates a Q global instead. | ||
// Montage Require | ||
if (typeof bootstrap === "function") { | ||
bootstrap("promise", definition); | ||
// CommonJS | ||
} else if (typeof exports === "object") { | ||
module.exports = definition(); | ||
// RequireJS | ||
} else if (typeof define === "function" && define.amd) { | ||
define(definition); | ||
// SES (Secure EcmaScript) | ||
} else if (typeof ses !== "undefined") { | ||
if (!ses.ok()) { | ||
return; | ||
} else { | ||
ses.makeQ = definition; | ||
} | ||
// <script> | ||
} else { | ||
Q = definition(); | ||
} | ||
})(function () { | ||
/*global -WeakMap */ | ||
"use strict"; | ||
@@ -79,226 +44,7 @@ | ||
// shims | ||
require("collections/shim"); | ||
var WeakMap = require("collections/weak-map"); | ||
var Iterator = require("collections/iterator"); | ||
var asap = require("asap"); | ||
// used for fallback in "allResolved" | ||
var noop = function () {}; | ||
// Use the fastest possible means to execute a task in a future turn | ||
// of the event loop. | ||
var nextTick =(function () { | ||
// linked list of tasks (single, with head node) | ||
var head = {task: void 0, next: null}; | ||
var tail = head; | ||
var flushing = false; | ||
var requestTick = void 0; | ||
var isNodeJS = false; | ||
function flush() { | ||
/* jshint loopfunc: true */ | ||
while (head.next) { | ||
head = head.next; | ||
var task = head.task; | ||
head.task = void 0; | ||
var domain = head.domain; | ||
if (domain) { | ||
head.domain = void 0; | ||
domain.enter(); | ||
} | ||
try { | ||
task(); | ||
} catch (e) { | ||
if (isNodeJS) { | ||
// In node, uncaught exceptions are considered fatal errors. | ||
// Re-throw them synchronously to interrupt flushing! | ||
// Ensure continuation if the uncaught exception is suppressed | ||
// listening "uncaughtException" events (as domains does). | ||
// Continue in next event to avoid tick recursion. | ||
if (domain) { | ||
domain.exit(); | ||
} | ||
setTimeout(flush, 0); | ||
if (domain) { | ||
domain.enter(); | ||
} | ||
throw e; | ||
} else { | ||
// In browsers, uncaught exceptions are not fatal. | ||
// Re-throw them asynchronously to avoid slow-downs. | ||
setTimeout(function() { | ||
throw e; | ||
}, 0); | ||
} | ||
} | ||
if (domain) { | ||
domain.exit(); | ||
} | ||
} | ||
flushing = false; | ||
} | ||
nextTick = function (task) { | ||
tail = tail.next = { | ||
task: task, | ||
domain: isNodeJS && process.domain, | ||
next: null | ||
}; | ||
if (!flushing) { | ||
flushing = true; | ||
requestTick(); | ||
} | ||
}; | ||
if (typeof process !== "undefined" && process.nextTick) { | ||
// Node.js before 0.9. Note that some fake-Node environments, like the | ||
// Mocha test runner, introduce a `process` global without a `nextTick`. | ||
isNodeJS = true; | ||
requestTick = function () { | ||
process.nextTick(flush); | ||
}; | ||
} else if (typeof setImmediate === "function") { | ||
// In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate | ||
if (typeof window !== "undefined") { | ||
requestTick = setImmediate.bind(window, flush); | ||
} else { | ||
requestTick = function () { | ||
setImmediate(flush); | ||
}; | ||
} | ||
} else if (typeof MessageChannel !== "undefined") { | ||
// modern browsers | ||
// http://www.nonblocking.io/2011/06/windownexttick.html | ||
var channel = new MessageChannel(); | ||
// At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create | ||
// working message ports the first time a page loads. | ||
channel.port1.onmessage = function () { | ||
requestTick = requestPortTick; | ||
channel.port1.onmessage = flush; | ||
flush(); | ||
}; | ||
var requestPortTick = function () { | ||
// Opera requires us to provide a message payload, regardless of | ||
// whether we use it. | ||
channel.port2.postMessage(0); | ||
}; | ||
requestTick = function () { | ||
setTimeout(flush, 0); | ||
requestPortTick(); | ||
}; | ||
} else { | ||
// old browsers | ||
requestTick = function () { | ||
setTimeout(flush, 0); | ||
}; | ||
} | ||
return nextTick; | ||
})(); | ||
// Attempt to make generics safe in the face of downstream | ||
// modifications. | ||
// There is no situation where this is necessary. | ||
// If you need a security guarantee, these primordials need to be | ||
// deeply frozen anyway, and if you don’t need a security guarantee, | ||
// this is just plain paranoid. | ||
// However, this **might** have the nice side-effect of reducing the size of | ||
// the minified code by reducing x.call() to merely x() | ||
// See Mark Miller’s explanation of what this does. | ||
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming | ||
var call = Function.call; | ||
function uncurryThis(f) { | ||
return function () { | ||
return call.apply(f, arguments); | ||
}; | ||
} | ||
// This is equivalent, but slower: | ||
// uncurryThis = Function_bind.bind(Function_bind.call); | ||
// http://jsperf.com/uncurrythis | ||
var array_slice = uncurryThis(Array.prototype.slice); | ||
var array_reduce = uncurryThis( | ||
Array.prototype.reduce || function (callback, basis) { | ||
var index = 0, | ||
length = this.length; | ||
// concerning the initial value, if one is not provided | ||
if (arguments.length === 1) { | ||
// seek to the first value in the array, accounting | ||
// for the possibility that is is a sparse array | ||
do { | ||
if (index in this) { | ||
basis = this[index++]; | ||
break; | ||
} | ||
if (++index >= length) { | ||
throw new TypeError(); | ||
} | ||
} while (1); | ||
} | ||
// reduce | ||
for (; index < length; index++) { | ||
// account for the possibility that the array is sparse | ||
if (index in this) { | ||
basis = callback(basis, this[index], index); | ||
} | ||
} | ||
return basis; | ||
} | ||
); | ||
var array_indexOf = uncurryThis( | ||
Array.prototype.indexOf || function (value) { | ||
// not a very good shim, but good enough for our one use of it | ||
for (var i = 0; i < this.length; i++) { | ||
if (this[i] === value) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
); | ||
var array_map = uncurryThis( | ||
Array.prototype.map || function (callback, thisp) { | ||
var self = this; | ||
var collect = []; | ||
array_reduce(self, function (undefined, value, index) { | ||
collect.push(callback.call(thisp, value, index, self)); | ||
}, void 0); | ||
return collect; | ||
} | ||
); | ||
var object_create = Object.create || function (prototype) { | ||
function Type() { } | ||
Type.prototype = prototype; | ||
return new Type(); | ||
}; | ||
var object_hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty); | ||
var object_keys = Object.keys || function (object) { | ||
var keys = []; | ||
for (var key in object) { | ||
if (object_hasOwnProperty(object, key)) { | ||
keys.push(key); | ||
} | ||
} | ||
return keys; | ||
}; | ||
var object_toString = uncurryThis(Object.prototype.toString); | ||
function isObject(value) { | ||
@@ -308,23 +54,2 @@ return value === Object(value); | ||
// generator related shims | ||
// FIXME: Remove this function once ES6 generators are in SpiderMonkey. | ||
function isStopIteration(exception) { | ||
return ( | ||
object_toString(exception) === "[object StopIteration]" || | ||
exception instanceof QReturnValue | ||
); | ||
} | ||
// FIXME: Remove this helper and Q.return once ES6 generators are in | ||
// SpiderMonkey. | ||
var QReturnValue; | ||
if (typeof ReturnValue !== "undefined") { | ||
QReturnValue = ReturnValue; | ||
} else { | ||
QReturnValue = function (value) { | ||
this.value = value; | ||
}; | ||
} | ||
// long stack traces | ||
@@ -345,3 +70,3 @@ | ||
var stacks = []; | ||
for (var p = promise; !!p; p = p.source) { | ||
for (var p = promise; !!p && handlers.get(p); p = handlers.get(p).became) { | ||
if (p.stack) { | ||
@@ -359,2 +84,5 @@ stacks.unshift(p.stack); | ||
function filterStackString(stackString) { | ||
if (Q.isIntrospective) { | ||
return stackString; | ||
} | ||
var lines = stackString.split("\n"); | ||
@@ -436,20 +164,63 @@ var desiredLines = []; | ||
function deprecate(callback, name, alternative) { | ||
return function () { | ||
if (typeof console !== "undefined" && | ||
typeof console.warn === "function") { | ||
console.warn(name + " is deprecated, use " + alternative + | ||
" instead.", new Error("").stack); | ||
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(callback, arguments); | ||
return callback.apply(this, arguments); | ||
}; | ||
} | ||
// end of shims | ||
// beginning of real work | ||
// end of long stack traces | ||
var handlers = new WeakMap(); | ||
function Q_inspect(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_inspect(theViciousCycleRejection); | ||
var thenables = new WeakMap(); | ||
/** | ||
* Constructs a promise for an immediate reference, passes promises through, or | ||
* coerces promises from different systems. | ||
* @param value immediate reference or promise | ||
* 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) { | ||
@@ -459,25 +230,30 @@ // If the object is already a Promise, return it directly. This enables | ||
// but to tolerably coerce non-promises to promises. | ||
if (isPromise(value)) { | ||
if (Q_isPromise(value)) { | ||
return value; | ||
} | ||
// assimilate thenables | ||
if (isPromiseAlike(value)) { | ||
return coerce(value); | ||
} else if (isThenable(value)) { | ||
if (!thenables.has(value)) { | ||
thenables.set(value, new Promise(new Thenable(value))); | ||
} | ||
return thenables.get(value); | ||
} else { | ||
return fulfill(value); | ||
return new Promise(new Fulfilled(value)); | ||
} | ||
} | ||
Q.resolve = Q; | ||
/** | ||
* Performs a task in a future turn of the event loop. | ||
* @param {Function} task | ||
* Controls whether or not long stack traces will be on | ||
* @type {boolean} | ||
*/ | ||
Q.nextTick = nextTick; | ||
Q.longStackSupport = false; | ||
/** | ||
* Controls whether or not long stack traces will be on | ||
* 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.longStackSupport = false; | ||
Q.reject = Q_reject; | ||
function Q_reject(error) { | ||
return new Promise(new Rejected(error)); | ||
} | ||
@@ -493,49 +269,12 @@ /** | ||
* `resolve` with that other thenable. | ||
* | ||
* @returns {{promise, resolve, reject}} a deferred | ||
*/ | ||
Q.defer = defer; | ||
function defer() { | ||
// 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. | ||
var messages = [], progressListeners = [], resolvedPromise; | ||
var deferred = object_create(defer.prototype); | ||
var promise = object_create(Promise.prototype); | ||
var handler = new Pending(); | ||
var promise = new Promise(handler); | ||
var deferred = new Deferred(promise); | ||
promise.promiseDispatch = function (resolve, op, operands) { | ||
var args = array_slice(arguments); | ||
if (messages) { | ||
messages.push(args); | ||
if (op === "when" && operands[1]) { // progress operand | ||
progressListeners.push(operands[1]); | ||
} | ||
} else { | ||
nextTick(function () { | ||
resolvedPromise.promiseDispatch.apply(resolvedPromise, args); | ||
}); | ||
} | ||
}; | ||
// XXX deprecated | ||
promise.valueOf = function () { | ||
if (messages) { | ||
return promise; | ||
} | ||
var nearerValue = nearer(resolvedPromise); | ||
if (isPromise(nearerValue)) { | ||
resolvedPromise = nearerValue; // shorten chain | ||
} | ||
return nearerValue; | ||
}; | ||
promise.inspect = function () { | ||
if (!resolvedPromise) { | ||
return { state: "pending" }; | ||
} | ||
return resolvedPromise.inspect(); | ||
}; | ||
if (Q.longStackSupport && hasStacks) { | ||
@@ -555,118 +294,164 @@ try { | ||
// NOTE: we do the checks for `resolvedPromise` in each method, instead of | ||
// consolidating them into `become`, since otherwise we'd create new | ||
// promises with the lines `become(whatever(value))`. See e.g. GH-252. | ||
return deferred; | ||
} | ||
function become(newPromise) { | ||
resolvedPromise = newPromise; | ||
promise.source = newPromise; | ||
// TODO | ||
/** | ||
*/ | ||
Q.when = function Q_when(value, fulfilled, rejected, ms) { | ||
return Q(value).then(fulfilled, rejected, ms); | ||
}; | ||
array_reduce(messages, function (undefined, message) { | ||
nextTick(function () { | ||
newPromise.promiseDispatch.apply(newPromise, message); | ||
}); | ||
}, void 0); | ||
messages = void 0; | ||
progressListeners = void 0; | ||
/** | ||
* 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_inspect(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 | ||
); | ||
deferred.promise = promise; | ||
deferred.resolve = function (value) { | ||
if (resolvedPromise) { | ||
return; | ||
} | ||
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; | ||
} | ||
}); | ||
become(Q(value)); | ||
}; | ||
deferred.fulfill = function (value) { | ||
if (resolvedPromise) { | ||
return; | ||
} | ||
}); | ||
become(fulfill(value)); | ||
}; | ||
deferred.reject = function (reason) { | ||
if (resolvedPromise) { | ||
return; | ||
function computeEstimate() { | ||
estimate = -Infinity; | ||
for (var index = 0; index < estimates.length; index++) { | ||
if (estimates[index] > estimate) { | ||
estimate = estimates[index]; | ||
} | ||
} | ||
} | ||
become(reject(reason)); | ||
}; | ||
deferred.notify = function (progress) { | ||
if (resolvedPromise) { | ||
return; | ||
} | ||
if (countDown === 0) { | ||
deferred.resolve(answers); | ||
} | ||
array_reduce(progressListeners, function (undefined, progressListener) { | ||
nextTick(function () { | ||
progressListener(progress); | ||
}); | ||
}, void 0); | ||
}; | ||
return deferred; | ||
return deferred.promise; | ||
} | ||
/** | ||
* Creates a Node-style callback that will resolve or reject the deferred | ||
* promise. | ||
* @returns a nodeback | ||
* @see Promise#allSettled | ||
*/ | ||
defer.prototype.makeNodeResolver = function () { | ||
var self = this; | ||
return function (error, value) { | ||
if (error) { | ||
self.reject(error); | ||
} else if (arguments.length > 2) { | ||
self.resolve(array_slice(arguments, 1)); | ||
} else { | ||
self.resolve(value); | ||
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); | ||
})); | ||
} | ||
/** | ||
* @param resolver {Function} a function that returns nothing and accepts | ||
* the resolve, reject, and notify functions for a deferred. | ||
* @returns a promise that may be resolved with the given resolve and reject | ||
* functions, or rejected by a thrown exception in resolver | ||
* 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.Promise = promise; // ES6 | ||
Q.promise = promise; | ||
function promise(resolver) { | ||
if (typeof resolver !== "function") { | ||
throw new TypeError("resolver must be a function."); | ||
Q.delay = function Q_delay(object, timeout) { | ||
if (timeout === void 0) { | ||
timeout = object; | ||
object = void 0; | ||
} | ||
var deferred = defer(); | ||
try { | ||
resolver(deferred.resolve, deferred.reject, deferred.notify); | ||
} catch (reason) { | ||
deferred.reject(reason); | ||
} | ||
return deferred.promise; | ||
} | ||
promise.race = race; // ES6 | ||
promise.all = all; // ES6 | ||
promise.reject = reject; // ES6 | ||
promise.resolve = Q; // ES6 | ||
// XXX experimental. This method is a way to denote that a local value is | ||
// serializable and should be immediately dispatched to a remote upon request, | ||
// instead of passing a reference. | ||
Q.passByCopy = function (object) { | ||
//freeze(object); | ||
//passByCopies.set(object, true); | ||
return object; | ||
return Q(object).delay(timeout); | ||
}; | ||
Promise.prototype.passByCopy = function () { | ||
//freeze(object); | ||
//passByCopies.set(object, true); | ||
return this; | ||
/** | ||
* 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, | ||
@@ -680,8 +465,4 @@ * but otherwise rejects. | ||
*/ | ||
Q.join = function (x, y) { | ||
return Q(x).join(y); | ||
}; | ||
Promise.prototype.join = function (that) { | ||
return Q([this, that]).spread(function (x, y) { | ||
Q.join = function Q_join(x, y) { | ||
return Q.spread([x, y], function Q_joined(x, y) { | ||
if (x === y) { | ||
@@ -698,471 +479,94 @@ // TODO: "===" should be Object.is or equiv | ||
* Returns a promise for the first of an array of promises to become fulfilled. | ||
* @param answers {Array[Any*]} promises to race | ||
* @returns {Any*} the first promise to be fulfilled | ||
* @param answers {Array} promises to race | ||
* @returns {Promise} the first promise to be fulfilled | ||
*/ | ||
Q.race = race; | ||
function race(answerPs) { | ||
return promise(function(resolve, reject) { | ||
// Switch to this once we can assume at least ES5 | ||
// answerPs.forEach(function(answerP) { | ||
// Q(answerP).then(resolve, reject); | ||
// }); | ||
// Use this in the meantime | ||
for (var i = 0, len = answerPs.length; i < len; i++) { | ||
Q(answerPs[i]).then(resolve, reject); | ||
} | ||
Q.race = Q_race; | ||
function Q_race(answerPs) { | ||
return new Promise(function(deferred) { | ||
answerPs.forEach(function(answerP) { | ||
Q(answerP).then(deferred.resolve, deferred.reject); | ||
}); | ||
}); | ||
} | ||
Promise.prototype.race = function () { | ||
return this.then(Q.race); | ||
/** | ||
* 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", [[]]); | ||
}; | ||
/** | ||
* Constructs a Promise with a promise descriptor object and optional fallback | ||
* function. The descriptor contains methods like when(rejected), get(name), | ||
* set(name, value), post(name, args), and delete(name), which all | ||
* return either a value, a promise for a value, or a rejection. The fallback | ||
* accepts the operation name, a resolver, and any further arguments that would | ||
* have been forwarded to the appropriate method above had a method been | ||
* provided with the proper name. The API makes no guarantees about the nature | ||
* of the returned object, apart from that it is usable whereever promises are | ||
* bought and sold. | ||
* TODO | ||
*/ | ||
Q.makePromise = Promise; | ||
function Promise(descriptor, fallback, inspect) { | ||
if (fallback === void 0) { | ||
fallback = function (op) { | ||
return reject(new Error( | ||
"Promise does not support operation: " + op | ||
)); | ||
}; | ||
} | ||
if (inspect === void 0) { | ||
inspect = function () { | ||
return {state: "unknown"}; | ||
}; | ||
} | ||
var promise = object_create(Promise.prototype); | ||
promise.promiseDispatch = function (resolve, op, args) { | ||
var result; | ||
try { | ||
if (descriptor[op]) { | ||
result = descriptor[op].apply(promise, args); | ||
} else { | ||
result = fallback.call(promise, op, args); | ||
} | ||
} catch (exception) { | ||
result = reject(exception); | ||
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]; | ||
} | ||
if (resolve) { | ||
resolve(result); | ||
} | ||
return Q(wrapped).apply(this, args); | ||
}; | ||
promise.inspect = inspect; | ||
// XXX deprecated `valueOf` and `exception` support | ||
if (inspect) { | ||
var inspected = inspect(); | ||
if (inspected.state === "rejected") { | ||
promise.exception = inspected.reason; | ||
} | ||
promise.valueOf = function () { | ||
var inspected = inspect(); | ||
if (inspected.state === "pending" || | ||
inspected.state === "rejected") { | ||
return promise; | ||
} | ||
return inspected.value; | ||
}; | ||
} | ||
return promise; | ||
} | ||
Promise.prototype.toString = function () { | ||
return "[object Promise]"; | ||
}; | ||
Promise.prototype.then = function (fulfilled, rejected, progressed) { | ||
var self = this; | ||
var deferred = defer(); | ||
var done = false; // ensure the untrusted promise makes at most a | ||
// single call to one of the callbacks | ||
function _fulfilled(value) { | ||
try { | ||
return typeof fulfilled === "function" ? fulfilled(value) : value; | ||
} catch (exception) { | ||
return reject(exception); | ||
} | ||
} | ||
function _rejected(exception) { | ||
if (typeof rejected === "function") { | ||
makeStackTraceLong(exception, self); | ||
try { | ||
return rejected(exception); | ||
} catch (newException) { | ||
return reject(newException); | ||
} | ||
} | ||
return reject(exception); | ||
} | ||
function _progressed(value) { | ||
return typeof progressed === "function" ? progressed(value) : value; | ||
} | ||
nextTick(function () { | ||
self.promiseDispatch(function (value) { | ||
if (done) { | ||
return; | ||
} | ||
done = true; | ||
deferred.resolve(_fulfilled(value)); | ||
}, "when", [function (exception) { | ||
if (done) { | ||
return; | ||
} | ||
done = true; | ||
deferred.resolve(_rejected(exception)); | ||
}]); | ||
}); | ||
// Progress propagator need to be attached in the current tick. | ||
self.promiseDispatch(void 0, "when", [void 0, function (value) { | ||
var newValue; | ||
var threw = false; | ||
try { | ||
newValue = _progressed(value); | ||
} catch (e) { | ||
threw = true; | ||
if (Q.onerror) { | ||
Q.onerror(e); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
if (!threw) { | ||
deferred.notify(newValue); | ||
} | ||
}]); | ||
return deferred.promise; | ||
}; | ||
/** | ||
* Registers an observer on a promise. | ||
* 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. | ||
* | ||
* Guarantees: | ||
* @example | ||
* var add = Q.promised(function (a, b) { | ||
* return a + b; | ||
* }); | ||
* add(Q(a), Q(B)); | ||
* | ||
* 1. that fulfilled and rejected will be called only once. | ||
* 2. that either the fulfilled callback or the rejected callback will be | ||
* called, but not both. | ||
* 3. that fulfilled and rejected will not be called in this turn. | ||
* | ||
* @param value promise or immediate reference to observe | ||
* @param fulfilled function to be called with the fulfilled value | ||
* @param rejected function to be called with the rejection exception | ||
* @param progressed function to be called on any progress notifications | ||
* @return promise for the return value from the invoked callback | ||
* @param {function} callback The function to decorate | ||
* @returns {function} a function that has been decorated. | ||
*/ | ||
Q.when = when; | ||
function when(value, fulfilled, rejected, progressed) { | ||
return Q(value).then(fulfilled, rejected, progressed); | ||
} | ||
Promise.prototype.thenResolve = function (value) { | ||
return this.then(function () { return value; }); | ||
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.thenResolve = function (promise, value) { | ||
return Q(promise).thenResolve(value); | ||
}; | ||
Promise.prototype.thenReject = function (reason) { | ||
return this.then(function () { throw reason; }); | ||
}; | ||
Q.thenReject = function (promise, reason) { | ||
return Q(promise).thenReject(reason); | ||
}; | ||
/** | ||
* If an object is not a promise, it is as "near" as possible. | ||
* If a promise is rejected, it is as "near" as possible too. | ||
* If it’s a fulfilled promise, the fulfillment value is nearer. | ||
* If it’s a deferred promise and the deferred has been resolved, the | ||
* resolution is "nearer". | ||
* @param object | ||
* @returns most resolved (nearest) form of the object | ||
*/ | ||
// XXX should we re-do this? | ||
Q.nearer = nearer; | ||
function nearer(value) { | ||
if (isPromise(value)) { | ||
var inspected = value.inspect(); | ||
if (inspected.state === "fulfilled") { | ||
return inspected.value; | ||
} | ||
Q.passByCopy = // TODO XXX experimental | ||
Q.push = function (value) { | ||
if (Object(value) === value && !Q_isPromise(value)) { | ||
passByCopies.set(value, true); | ||
} | ||
return value; | ||
} | ||
/** | ||
* @returns whether the given object is a promise. | ||
* Otherwise it is a fulfilled value. | ||
*/ | ||
Q.isPromise = isPromise; | ||
function isPromise(object) { | ||
return isObject(object) && | ||
typeof object.promiseDispatch === "function" && | ||
typeof object.inspect === "function"; | ||
} | ||
Q.isPromiseAlike = isPromiseAlike; | ||
function isPromiseAlike(object) { | ||
return isObject(object) && typeof object.then === "function"; | ||
} | ||
/** | ||
* @returns whether the given object is a pending promise, meaning not | ||
* fulfilled or rejected. | ||
*/ | ||
Q.isPending = isPending; | ||
function isPending(object) { | ||
return isPromise(object) && object.inspect().state === "pending"; | ||
} | ||
Promise.prototype.isPending = function () { | ||
return this.inspect().state === "pending"; | ||
}; | ||
/** | ||
* @returns whether the given object is a value or fulfilled | ||
* promise. | ||
*/ | ||
Q.isFulfilled = isFulfilled; | ||
function isFulfilled(object) { | ||
return !isPromise(object) || object.inspect().state === "fulfilled"; | ||
} | ||
Promise.prototype.isFulfilled = function () { | ||
return this.inspect().state === "fulfilled"; | ||
Q.isPortable = function (value) { | ||
return Object(value) === value && passByCopies.has(value); | ||
}; | ||
/** | ||
* @returns whether the given object is a rejected promise. | ||
*/ | ||
Q.isRejected = isRejected; | ||
function isRejected(object) { | ||
return isPromise(object) && object.inspect().state === "rejected"; | ||
} | ||
var passByCopies = new WeakMap(); | ||
Promise.prototype.isRejected = function () { | ||
return this.inspect().state === "rejected"; | ||
}; | ||
//// BEGIN UNHANDLED REJECTION TRACKING | ||
// This promise library consumes exceptions thrown in handlers so they can be | ||
// handled by a subsequent promise. The exceptions get added to this array when | ||
// they are created, and removed when they are handled. Note that in ES6 or | ||
// shimmed environments, this would naturally be a `Set`. | ||
var unhandledReasons = []; | ||
var unhandledRejections = []; | ||
var trackUnhandledRejections = true; | ||
function resetUnhandledRejections() { | ||
unhandledReasons.length = 0; | ||
unhandledRejections.length = 0; | ||
if (!trackUnhandledRejections) { | ||
trackUnhandledRejections = true; | ||
} | ||
} | ||
function trackRejection(promise, reason) { | ||
if (!trackUnhandledRejections) { | ||
return; | ||
} | ||
unhandledRejections.push(promise); | ||
if (reason && typeof reason.stack !== "undefined") { | ||
unhandledReasons.push(reason.stack); | ||
} else { | ||
unhandledReasons.push("(no stack) " + reason); | ||
} | ||
} | ||
function untrackRejection(promise) { | ||
if (!trackUnhandledRejections) { | ||
return; | ||
} | ||
var at = array_indexOf(unhandledRejections, promise); | ||
if (at !== -1) { | ||
unhandledRejections.splice(at, 1); | ||
unhandledReasons.splice(at, 1); | ||
} | ||
} | ||
Q.resetUnhandledRejections = resetUnhandledRejections; | ||
Q.getUnhandledReasons = function () { | ||
// Make a copy so that consumers can't interfere with our internal state. | ||
return unhandledReasons.slice(); | ||
}; | ||
Q.stopUnhandledRejectionTracking = function () { | ||
resetUnhandledRejections(); | ||
trackUnhandledRejections = false; | ||
}; | ||
resetUnhandledRejections(); | ||
//// END UNHANDLED REJECTION TRACKING | ||
/** | ||
* Constructs a rejected promise. | ||
* @param reason value describing the failure | ||
*/ | ||
Q.reject = reject; | ||
function reject(reason) { | ||
var rejection = Promise({ | ||
"when": function (rejected) { | ||
// note that the error has been handled | ||
if (rejected) { | ||
untrackRejection(this); | ||
} | ||
return rejected ? rejected(reason) : this; | ||
} | ||
}, function fallback() { | ||
return this; | ||
}, function inspect() { | ||
return { state: "rejected", reason: reason }; | ||
}); | ||
// Note that the reason has not been handled. | ||
trackRejection(rejection, reason); | ||
return rejection; | ||
} | ||
/** | ||
* Constructs a fulfilled promise for an immediate reference. | ||
* @param value immediate reference | ||
*/ | ||
Q.fulfill = fulfill; | ||
function fulfill(value) { | ||
return Promise({ | ||
"when": function () { | ||
return value; | ||
}, | ||
"get": function (name) { | ||
return value[name]; | ||
}, | ||
"set": function (name, rhs) { | ||
value[name] = rhs; | ||
}, | ||
"delete": function (name) { | ||
delete value[name]; | ||
}, | ||
"post": function (name, args) { | ||
// Mark Miller proposes that post with no name should apply a | ||
// promised function. | ||
if (name === null || name === void 0) { | ||
return value.apply(void 0, args); | ||
} else { | ||
return value[name].apply(value, args); | ||
} | ||
}, | ||
"apply": function (thisp, args) { | ||
return value.apply(thisp, args); | ||
}, | ||
"keys": function () { | ||
return object_keys(value); | ||
} | ||
}, void 0, function inspect() { | ||
return { state: "fulfilled", value: value }; | ||
}); | ||
} | ||
/** | ||
* Converts thenables to Q promises. | ||
* @param promise thenable promise | ||
* @returns a Q promise | ||
*/ | ||
function coerce(promise) { | ||
var deferred = defer(); | ||
nextTick(function () { | ||
try { | ||
promise.then(deferred.resolve, deferred.reject, deferred.notify); | ||
} catch (exception) { | ||
deferred.reject(exception); | ||
} | ||
}); | ||
return deferred.promise; | ||
} | ||
/** | ||
* Annotates an object such that it will never be | ||
* transferred away from this process over any promise | ||
* communication channel. | ||
* @param object | ||
* @returns promise a wrapping of that object that | ||
* additionally responds to the "isDef" message | ||
* without a rejection. | ||
*/ | ||
Q.master = master; | ||
function master(object) { | ||
return Promise({ | ||
"isDef": function () {} | ||
}, function fallback(op, args) { | ||
return dispatch(object, op, args); | ||
}, function () { | ||
return Q(object).inspect(); | ||
}); | ||
} | ||
/** | ||
* 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 = spread; | ||
function spread(value, fulfilled, rejected) { | ||
return Q(value).spread(fulfilled, rejected); | ||
} | ||
Promise.prototype.spread = function (fulfilled, rejected) { | ||
return this.all().then(function (array) { | ||
return fulfilled.apply(void 0, array); | ||
}, rejected); | ||
}; | ||
/** | ||
* 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. | ||
* 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. SpiderMonkey has had them | ||
* for longer, but under an older Python-inspired form. This function | ||
* works on both kinds of generators. | ||
* `--harmony-generators` runtime flag enabled. This function does not | ||
* support the former, Pythonic generators that were only implemented | ||
* by SpiderMonkey. | ||
* | ||
@@ -1183,43 +587,18 @@ * Decorates a generator function such that: | ||
*/ | ||
Q.async = async; | ||
function async(makeGenerator) { | ||
return function () { | ||
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 result; | ||
// Until V8 3.19 / Chromium 29 is released, SpiderMonkey is the only | ||
// engine that has a deployed base of browsers that support generators. | ||
// However, SM's generators use the Python-inspired semantics of | ||
// outdated ES6 drafts. We would like to support ES6, but we'd also | ||
// like to make it possible to use generators in deployed browsers, so | ||
// we also support Python-style generators. At some point we can remove | ||
// this block. | ||
if (typeof StopIteration === "undefined") { | ||
// ES6 Generators | ||
try { | ||
result = generator[verb](arg); | ||
} catch (exception) { | ||
return reject(exception); | ||
} | ||
if (result.done) { | ||
return result.value; | ||
} else { | ||
return when(result.value, callback, errback); | ||
} | ||
var iteration; | ||
try { | ||
iteration = generator[verb](arg); | ||
} catch (exception) { | ||
return Q_reject(exception); | ||
} | ||
if (iteration.done) { | ||
return Q(iteration.value); | ||
} else { | ||
// SpiderMonkey Generators | ||
// FIXME: Remove this case when SM does ES6 generators. | ||
try { | ||
result = generator[verb](arg); | ||
} catch (exception) { | ||
if (isStopIteration(exception)) { | ||
return exception.value; | ||
} else { | ||
return reject(exception); | ||
} | ||
} | ||
return when(result, callback, errback); | ||
return Q(iteration.value).then(callback, errback); | ||
} | ||
@@ -1241,318 +620,298 @@ } | ||
*/ | ||
Q.spawn = spawn; | ||
function spawn(makeGenerator) { | ||
Q.done(Q.async(makeGenerator)()); | ||
Q.spawn = Q_spawn; | ||
function Q_spawn(makeGenerator) { | ||
Q_async(makeGenerator)().done(); | ||
} | ||
// FIXME: Remove this interface once ES6 generators are in SpiderMonkey. | ||
// Thus begins the section dedicated to the Promise | ||
/** | ||
* Throws a ReturnValue exception to stop an asynchronous generator. | ||
* | ||
* This interface is a stop-gap measure to support generator return | ||
* values in older Firefox/SpiderMonkey. In browsers that support ES6 | ||
* generators like Chromium 29, just use "return" in your generator | ||
* functions. | ||
* | ||
* @param value the return value for the surrounding generator | ||
* @throws ReturnValue exception with the value. | ||
* @example | ||
* // ES6 style | ||
* Q.async(function* () { | ||
* var foo = yield getFooPromise(); | ||
* var bar = yield getBarPromise(); | ||
* return foo + bar; | ||
* }) | ||
* // Older SpiderMonkey style | ||
* Q.async(function () { | ||
* var foo = yield getFooPromise(); | ||
* var bar = yield getBarPromise(); | ||
* Q.return(foo + bar); | ||
* }) | ||
* TODO | ||
*/ | ||
Q["return"] = _return; | ||
function _return(value) { | ||
throw new QReturnValue(value); | ||
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_inspect(deferred.promise); | ||
try { | ||
setup(deferred.resolve, deferred.reject, deferred.setEstimate); | ||
} catch (error) { | ||
deferred.reject(error); | ||
} | ||
} | ||
handlers.set(this, handler); | ||
} | ||
/** | ||
* 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. | ||
* 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 | ||
*/ | ||
Q.promised = promised; | ||
function promised(callback) { | ||
return function () { | ||
return spread([this, all(arguments)], function (self, args) { | ||
return callback.apply(self, args); | ||
}); | ||
}; | ||
} | ||
Promise.all = Q_all; | ||
/** | ||
* sends a message to a value in a future turn | ||
* @param object* the recipient | ||
* @param op the name of the message operation, e.g., "when", | ||
* @param args further arguments to be forwarded to the operation | ||
* @returns result {Promise} a promise for the result of the operation | ||
* 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.dispatch = dispatch; | ||
function dispatch(object, op, args) { | ||
return Q(object).dispatch(op, args); | ||
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); | ||
} | ||
Promise.prototype.dispatch = function (op, args) { | ||
var self = this; | ||
var deferred = defer(); | ||
nextTick(function () { | ||
self.promiseDispatch(deferred.resolve, op, args); | ||
}); | ||
return deferred.promise; | ||
}; | ||
/** | ||
* Gets the value of a property in a future turn. | ||
* @param object promise or immediate reference for target object | ||
* @param name name of property to get | ||
* @return promise for the property value | ||
* 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 | ||
*/ | ||
Q.get = function (object, key) { | ||
return Q(object).dispatch("get", [key]); | ||
}; | ||
Promise.reject = Q_reject; | ||
Promise.prototype.get = function (key) { | ||
return this.dispatch("get", [key]); | ||
}; | ||
/** | ||
* Sets the value of a property in a future turn. | ||
* @param object promise or immediate reference for object object | ||
* @param name name of property to set | ||
* @param value new value of property | ||
* @return promise for the return value | ||
* @returns {boolean} whether the given value is a promise. | ||
*/ | ||
Q.set = function (object, key, value) { | ||
return Q(object).dispatch("set", [key, value]); | ||
}; | ||
Q.isPromise = Q_isPromise; | ||
function Q_isPromise(object) { | ||
return isObject(object) && !!handlers.get(object); | ||
} | ||
Promise.prototype.set = function (key, value) { | ||
return this.dispatch("set", [key, value]); | ||
}; | ||
/** | ||
* Deletes a property in a future turn. | ||
* @param object promise or immediate reference for target object | ||
* @param name name of property to delete | ||
* @return promise for the return value | ||
* @returns {boolean} whether the given value is an object with a then method. | ||
* @private | ||
*/ | ||
Q.del = // XXX legacy | ||
Q["delete"] = function (object, key) { | ||
return Q(object).dispatch("delete", [key]); | ||
}; | ||
function isThenable(object) { | ||
return isObject(object) && typeof object.then === "function"; | ||
} | ||
Promise.prototype.del = // XXX legacy | ||
Promise.prototype["delete"] = function (key) { | ||
return this.dispatch("delete", [key]); | ||
}; | ||
/** | ||
* Invokes a method in a future turn. | ||
* @param object promise or immediate reference for target object | ||
* @param name name of method to invoke | ||
* @param value a value to post, typically an array of | ||
* invocation arguments for promises that | ||
* are ultimately backed with `resolve` values, | ||
* as opposed to those backed with URLs | ||
* wherein the posted value can be any | ||
* JSON serializable object. | ||
* @return promise for the return value | ||
* 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?}} | ||
*/ | ||
// bound locally because it is used by other methods | ||
Q.mapply = // XXX As proposed by "Redsandro" | ||
Q.post = function (object, name, args) { | ||
return Q(object).dispatch("post", [name, args]); | ||
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_inspect(this).inspect(); | ||
}; | ||
Promise.prototype.mapply = // XXX As proposed by "Redsandro" | ||
Promise.prototype.post = function (name, args) { | ||
return this.dispatch("post", [name, args]); | ||
}; | ||
/** | ||
* Invokes a method in a future turn. | ||
* @param object promise or immediate reference for target object | ||
* @param name name of method to invoke | ||
* @param ...args array of invocation arguments | ||
* @return promise for the return value | ||
* @returns {boolean} whether the promise is waiting for a result. | ||
*/ | ||
Q.send = // XXX Mark Miller's proposed parlance | ||
Q.mcall = // XXX As proposed by "Redsandro" | ||
Q.invoke = function (object, name /*...args*/) { | ||
return Q(object).dispatch("post", [name, array_slice(arguments, 2)]); | ||
Promise.prototype.isPending = function Promise_isPending() { | ||
return Q_inspect(this).state === "pending"; | ||
}; | ||
Promise.prototype.send = // XXX Mark Miller's proposed parlance | ||
Promise.prototype.mcall = // XXX As proposed by "Redsandro" | ||
Promise.prototype.invoke = function (name /*...args*/) { | ||
return this.dispatch("post", [name, array_slice(arguments, 1)]); | ||
}; | ||
/** | ||
* Applies the promised function in a future turn. | ||
* @param object promise or immediate reference for target function | ||
* @param args array of application arguments | ||
* @returns {boolean} whether the promise has ended in a result and has a | ||
* fulfillment value. | ||
*/ | ||
Q.fapply = function (object, args) { | ||
return Q(object).dispatch("apply", [void 0, args]); | ||
Promise.prototype.isFulfilled = function Promise_isFulfilled() { | ||
return Q_inspect(this).state === "fulfilled"; | ||
}; | ||
Promise.prototype.fapply = function (args) { | ||
return this.dispatch("apply", [void 0, args]); | ||
}; | ||
/** | ||
* Calls the promised function in a future turn. | ||
* @param object promise or immediate reference for target function | ||
* @param ...args array of application arguments | ||
* @returns {boolean} whether the promise has ended poorly and has a reason for | ||
* its rejection. | ||
*/ | ||
Q["try"] = | ||
Q.fcall = function (object /* ...args*/) { | ||
return Q(object).dispatch("apply", [void 0, array_slice(arguments, 1)]); | ||
Promise.prototype.isRejected = function Promise_isRejected() { | ||
return Q_inspect(this).state === "rejected"; | ||
}; | ||
Promise.prototype.fcall = function (/*...args*/) { | ||
return this.dispatch("apply", [void 0, array_slice(arguments)]); | ||
}; | ||
/** | ||
* Binds the promised function, transforming return values into a fulfilled | ||
* promise and thrown errors into a rejected one. | ||
* @param object promise or immediate reference for target function | ||
* @param ...args array of application arguments | ||
* @returns {string} merely `"[object Promise]"` | ||
*/ | ||
Q.fbind = function (object /*...args*/) { | ||
var promise = Q(object); | ||
var args = array_slice(arguments, 1); | ||
return function fbound() { | ||
return promise.dispatch("apply", [ | ||
this, | ||
args.concat(array_slice(arguments)) | ||
]); | ||
}; | ||
Promise.prototype.toString = function Promise_toString() { | ||
return "[object Promise]"; | ||
}; | ||
Promise.prototype.fbind = function (/*...args*/) { | ||
var promise = this; | ||
var args = array_slice(arguments); | ||
return function fbound() { | ||
return promise.dispatch("apply", [ | ||
this, | ||
args.concat(array_slice(arguments)) | ||
]); | ||
}; | ||
}; | ||
/** | ||
* Requests the names of the owned properties of a promised | ||
* object in a future turn. | ||
* @param object promise or immediate reference for target object | ||
* @return promise for the keys of the eventually settled object | ||
* 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`. | ||
*/ | ||
Q.keys = function (object) { | ||
return Q(object).dispatch("keys", []); | ||
}; | ||
Promise.prototype.then = function Promise_then(fulfilled, rejected, ms) { | ||
var self = this; | ||
var deferred = defer(); | ||
Promise.prototype.keys = function () { | ||
return this.dispatch("keys", []); | ||
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; | ||
}; | ||
/** | ||
* Turns an array of promises into a promise for an array. If any of | ||
* the promises gets rejected, the whole array is rejected immediately. | ||
* @param {Array*} an array (or promise for an array) of values (or | ||
* promises for values) | ||
* @returns a promise for an array of the corresponding values | ||
* Terminates a chain of promises, forcing rejections to be | ||
* thrown as exceptions. | ||
* @param fulfilled | ||
* @param rejected | ||
*/ | ||
// By Mark Miller | ||
// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled | ||
Q.all = all; | ||
function all(promises) { | ||
return when(promises, function (promises) { | ||
var countDown = 0; | ||
var deferred = defer(); | ||
array_reduce(promises, function (undefined, promise, index) { | ||
var snapshot; | ||
if ( | ||
isPromise(promise) && | ||
(snapshot = promise.inspect()).state === "fulfilled" | ||
) { | ||
promises[index] = snapshot.value; | ||
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 { | ||
++countDown; | ||
when( | ||
promise, | ||
function (value) { | ||
promises[index] = value; | ||
if (--countDown === 0) { | ||
deferred.resolve(promises); | ||
} | ||
}, | ||
deferred.reject, | ||
function (progress) { | ||
deferred.notify({ index: index, value: progress }); | ||
_fulfilled = function Promise_done_fulfilled(value) { | ||
if (done) { | ||
return; | ||
} | ||
); | ||
done = true; | ||
fulfilled.call(void 0, value); | ||
}; | ||
} | ||
}, void 0); | ||
if (countDown === 0) { | ||
deferred.resolve(promises); | ||
} | ||
return deferred.promise; | ||
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_inspect(self).dispatch(_fulfilled, "then", [_rejected]); | ||
}); | ||
}; | ||
function Promise_rethrow(error) { | ||
throw error; | ||
} | ||
Promise.prototype.all = function () { | ||
return all(this); | ||
/** | ||
* 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. | ||
}; | ||
/** | ||
* Waits for all promises to be settled, either fulfilled or | ||
* rejected. This is distinct from `all` since that would stop | ||
* waiting at the first rejection. The promise returned by | ||
* `allResolved` will never be rejected. | ||
* @param promises a promise for an array (or an array) of promises | ||
* (or values) | ||
* @return a promise for an array of promises | ||
* TODO | ||
*/ | ||
Q.allResolved = deprecate(allResolved, "allResolved", "allSettled"); | ||
function allResolved(promises) { | ||
return when(promises, function (promises) { | ||
promises = array_map(promises, Q); | ||
return when(all(array_map(promises, function (promise) { | ||
return when(promise, noop, noop); | ||
})), function () { | ||
return promises; | ||
}); | ||
}); | ||
} | ||
Promise.prototype.allResolved = function () { | ||
return allResolved(this); | ||
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. | ||
}; | ||
/** | ||
* @see Promise#allSettled | ||
* TODO | ||
*/ | ||
Q.allSettled = allSettled; | ||
function allSettled(promises) { | ||
return Q(promises).allSettled(); | ||
} | ||
Promise.prototype.all = function Promise_all() { | ||
return this.then(Q_all); | ||
}; | ||
@@ -1566,78 +925,63 @@ /** | ||
*/ | ||
Promise.prototype.allSettled = function () { | ||
return this.then(function (promises) { | ||
return all(array_map(promises, function (promise) { | ||
promise = Q(promise); | ||
function regardless() { | ||
return promise.inspect(); | ||
} | ||
return promise.then(regardless, regardless); | ||
})); | ||
}); | ||
Promise.prototype.allSettled = function Promise_allSettled() { | ||
return this.then(Q_allSettled); | ||
}; | ||
/** | ||
* Captures the failure of a promise, giving an oportunity to recover | ||
* with a callback. If the given promise is fulfilled, the returned | ||
* promise is fulfilled. | ||
* @param {Any*} promise for something | ||
* @param {Function} callback to fulfill the returned promise if the | ||
* given promise is rejected | ||
* @returns a promise for the return value of the callback | ||
* TODO | ||
*/ | ||
Q.fail = // XXX legacy | ||
Q["catch"] = function (object, rejected) { | ||
return Q(object).then(void 0, rejected); | ||
Promise.prototype.catch = function Promise_catch(rejected) { | ||
return this.then(void 0, rejected); | ||
}; | ||
Promise.prototype.fail = // XXX legacy | ||
Promise.prototype["catch"] = function (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); | ||
}; | ||
/** | ||
* Attaches a listener that can respond to progress notifications from a | ||
* promise's originating deferred. This listener receives the exact arguments | ||
* passed to ``deferred.notify``. | ||
* @param {Any*} promise for something | ||
* @param {Function} callback to receive any progress notifications | ||
* @returns the given promise, unchanged | ||
* TODO | ||
*/ | ||
Q.progress = progress; | ||
function progress(object, progressed) { | ||
return Q(object).then(void 0, void 0, progressed); | ||
} | ||
Promise.prototype.observeEstimate = function Promise_observeEstimate(emit) { | ||
this.dispatch("estimate", [emit]); | ||
return this; | ||
}; | ||
Promise.prototype.progress = function (progressed) { | ||
return this.then(void 0, void 0, progressed); | ||
/** | ||
* TODO | ||
*/ | ||
Promise.prototype.getEstimate = function Promise_getEstimate() { | ||
return Q_inspect(this).estimate; | ||
}; | ||
/** | ||
* Provides an opportunity to observe the settling of a promise, | ||
* regardless of whether the promise is fulfilled or rejected. Forwards | ||
* the resolution to the returned promise when the callback is done. | ||
* The callback can return a promise to defer completion. | ||
* @param {Any*} promise | ||
* @param {Function} callback to observe the resolution of the given | ||
* promise, takes no arguments. | ||
* @returns a promise for the resolution of the given promise when | ||
* ``fin`` is done. | ||
* TODO | ||
*/ | ||
Q.fin = // XXX legacy | ||
Q["finally"] = function (object, callback) { | ||
return Q(object)["finally"](callback); | ||
Promise.prototype.dispatch = function Promise_dispatch(op, args) { | ||
var deferred = defer(); | ||
this.rawDispatch(deferred.resolve, op, args); | ||
return deferred.promise; | ||
}; | ||
Promise.prototype.fin = // XXX legacy | ||
Promise.prototype["finally"] = function (callback) { | ||
callback = Q(callback); | ||
return this.then(function (value) { | ||
return callback.fcall().then(function () { | ||
return value; | ||
}); | ||
}, function (reason) { | ||
// TODO attempt to recycle the rejection with "this". | ||
return callback.fcall().then(function () { | ||
throw reason; | ||
}); | ||
/** | ||
*/ | ||
Promise.prototype.rawDispatch = function Promise_rawDispatch(resolve, op, args) { | ||
var self = this; | ||
asap(function Promise_dispatch_task() { | ||
Q_inspect(self).dispatch(resolve, op, args); | ||
}); | ||
@@ -1647,41 +991,81 @@ }; | ||
/** | ||
* Terminates a chain of promises, forcing rejections to be | ||
* thrown as exceptions. | ||
* @param {Any*} promise at the end of a chain of promises | ||
* @returns nothing | ||
* TODO | ||
*/ | ||
Q.done = function (object, fulfilled, rejected, progress) { | ||
return Q(object).done(fulfilled, rejected, progress); | ||
Promise.prototype.get = function Promise_get(key) { | ||
return this.dispatch("get", [key]); | ||
}; | ||
Promise.prototype.done = function (fulfilled, rejected, progress) { | ||
var onUnhandledError = function (error) { | ||
// forward to a future turn so that ``when`` | ||
// does not catch it and turn it into a rejection. | ||
nextTick(function () { | ||
makeStackTraceLong(error, promise); | ||
if (Q.onerror) { | ||
Q.onerror(error); | ||
} else { | ||
throw error; | ||
} | ||
}); | ||
}; | ||
/** | ||
* 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]); | ||
}; | ||
// Avoid unnecessary `nextTick`ing via an unnecessary `when`. | ||
var promise = fulfilled || rejected || progress ? | ||
this.then(fulfilled, rejected, progress) : | ||
this; | ||
/** | ||
* TODO | ||
*/ | ||
Promise.prototype.apply = function Promise_apply(thisp, args) { | ||
return this.dispatch("call", [args, thisp]); | ||
}; | ||
if (typeof process === "object" && process && process.domain) { | ||
onUnhandledError = process.domain.bind(onUnhandledError); | ||
/** | ||
* 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]); | ||
}; | ||
promise.then(void 0, onUnhandledError); | ||
/** | ||
* 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 {Any*} promise | ||
* @param {Number} milliseconds timeout | ||
@@ -1692,19 +1076,15 @@ * @param {String} custom error message (optional) | ||
*/ | ||
Q.timeout = function (object, ms, message) { | ||
return Q(object).timeout(ms, message); | ||
}; | ||
Promise.prototype.timeout = function (ms, message) { | ||
Promise.prototype.timeout = function Promsie_timeout(ms, message) { | ||
var deferred = defer(); | ||
var timeoutId = setTimeout(function () { | ||
var timeoutId = setTimeout(function Promise_timeout_task() { | ||
deferred.reject(new Error(message || "Timed out after " + ms + " ms")); | ||
}, ms); | ||
this.then(function (value) { | ||
this.then(function Promise_timeout_fulfilled(value) { | ||
clearTimeout(timeoutId); | ||
deferred.resolve(value); | ||
}, function (exception) { | ||
}, function Promise_timeout_rejected(error) { | ||
clearTimeout(timeoutId); | ||
deferred.reject(exception); | ||
}, deferred.notify); | ||
deferred.reject(error); | ||
}); | ||
@@ -1723,133 +1103,300 @@ return deferred.promise; | ||
*/ | ||
Q.delay = function (object, timeout) { | ||
if (timeout === void 0) { | ||
timeout = object; | ||
object = void 0; | ||
} | ||
return Q(object).delay(timeout); | ||
}; | ||
Promise.prototype.delay = function (timeout) { | ||
return this.then(function (value) { | ||
Promise.prototype.delay = function Promise_delay(ms) { | ||
return this.then(function Promise_delay_fulfilled(value) { | ||
var deferred = defer(); | ||
setTimeout(function () { | ||
deferred.setEstimate(Date.now() + ms); | ||
setTimeout(function Promise_delay_task() { | ||
deferred.resolve(value); | ||
}, timeout); | ||
}, ms); | ||
return deferred.promise; | ||
}); | ||
}, null, ms); | ||
}; | ||
/** | ||
* Passes a continuation to a Node function, which is called with the given | ||
* arguments provided as an array, and returns a promise. | ||
* | ||
* Q.nfapply(FS.readFile, [__filename]) | ||
* .then(function (content) { | ||
* }) | ||
* | ||
* TODO | ||
*/ | ||
Q.nfapply = function (callback, args) { | ||
return Q(callback).nfapply(args); | ||
Promise.prototype.pull = function Promise_pull() { | ||
return this.dispatch("pull", []); | ||
}; | ||
Promise.prototype.nfapply = function (args) { | ||
var deferred = defer(); | ||
var nodeArgs = array_slice(args); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
this.fapply(nodeArgs).fail(deferred.reject); | ||
return deferred.promise; | ||
}; | ||
// 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); | ||
}; | ||
} | ||
/** | ||
* Passes a continuation to a Node function, which is called with the given | ||
* arguments provided individually, and returns a promise. | ||
* @example | ||
* Q.nfcall(FS.readFile, __filename) | ||
* .then(function (content) { | ||
* }) | ||
* | ||
* TODO | ||
*/ | ||
Q.nfcall = function (callback /*...args*/) { | ||
var args = array_slice(arguments, 1); | ||
return Q(callback).nfapply(args); | ||
Deferred.prototype.resolve = function Deferred_resolve(value) { | ||
var handler = Q_inspect(promises.get(this)); | ||
if (!handler.messages) { | ||
return; | ||
} | ||
handler.become(Q(value)); | ||
}; | ||
Promise.prototype.nfcall = function (/*...args*/) { | ||
var nodeArgs = array_slice(arguments); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
this.fapply(nodeArgs).fail(deferred.reject); | ||
return deferred.promise; | ||
/** | ||
* TODO | ||
*/ | ||
Deferred.prototype.reject = function Deferred_reject(reason) { | ||
var handler = Q_inspect(promises.get(this)); | ||
if (!handler.messages) { | ||
return; | ||
} | ||
handler.become(Q_reject(reason)); | ||
}; | ||
/** | ||
* Wraps a NodeJS continuation passing function and returns an equivalent | ||
* version that returns a promise. | ||
* @example | ||
* Q.nfbind(FS.readFile, __filename)("utf-8") | ||
* .then(console.log) | ||
* .done() | ||
* TODO | ||
*/ | ||
Q.nfbind = | ||
Q.denodeify = function (callback /*...args*/) { | ||
var baseArgs = array_slice(arguments, 1); | ||
return function () { | ||
var nodeArgs = baseArgs.concat(array_slice(arguments)); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
Q(callback).fapply(nodeArgs).fail(deferred.reject); | ||
return deferred.promise; | ||
}; | ||
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_inspect(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); | ||
} | ||
}; | ||
Promise.prototype.nfbind = | ||
Promise.prototype.denodeify = function (/*...args*/) { | ||
var args = array_slice(arguments); | ||
args.unshift(this); | ||
return Q.denodeify.apply(void 0, args); | ||
// 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}; | ||
}; | ||
Q.nbind = function (callback, thisp /*...args*/) { | ||
var baseArgs = array_slice(arguments, 2); | ||
return function () { | ||
var nodeArgs = baseArgs.concat(array_slice(arguments)); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
function bound() { | ||
return callback.apply(thisp, arguments); | ||
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); | ||
} | ||
Q(bound).fapply(nodeArgs).fail(deferred.reject); | ||
return deferred.promise; | ||
}; | ||
} 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); | ||
} | ||
}; | ||
Promise.prototype.nbind = function (/*thisp, ...args*/) { | ||
var args = array_slice(arguments, 0); | ||
args.unshift(this); | ||
return Q.nbind.apply(void 0, args); | ||
Fulfilled.prototype.then = function Fulfilled_then() { | ||
return this.value; | ||
}; | ||
/** | ||
* Calls a method of a Node-style object that accepts a Node-style | ||
* callback with a given array of arguments, plus a provided callback. | ||
* @param object an object that has the named method | ||
* @param {String} name name of the method of object | ||
* @param {Array} args arguments to pass to the method; the callback | ||
* will be provided by Q and appended to these arguments. | ||
* @returns a promise for the value or error | ||
*/ | ||
Q.nmapply = // XXX As proposed by "Redsandro" | ||
Q.npost = function (object, name, args) { | ||
return Q(object).npost(name, args); | ||
Fulfilled.prototype.get = function Fulfilled_get(name) { | ||
return this.value[name]; | ||
}; | ||
Promise.prototype.nmapply = // XXX As proposed by "Redsandro" | ||
Promise.prototype.npost = function (name, args) { | ||
var nodeArgs = array_slice(args || []); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
this.dispatch("post", [name, nodeArgs]).fail(deferred.reject); | ||
return deferred.promise; | ||
Fulfilled.prototype.invoke = function Fulfilled_invoke( | ||
name, args | ||
) { | ||
return this.value[name].apply(this.value, args); | ||
}; | ||
Fulfilled.prototype.call = function Fulfilled_call(args, thisp) { | ||
return this.value.apply(thisp, args); | ||
}; | ||
Fulfilled.prototype.keys = function Fulfilled_keys() { | ||
return Object.keys(this.value); | ||
}; | ||
Fulfilled.prototype.iterate = function Fulfilled_iterate() { | ||
return new Iterator(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_inspect(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_inspect(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_inspect(deferred.promise); | ||
} | ||
return this.became; | ||
}; | ||
Thenable.prototype.dispatch = function Thenable_dispatch(resolve, op, args) { | ||
this.cast().dispatch(resolve, op, args); | ||
}; | ||
// Thus begins the Q Node.js bridge | ||
/** | ||
@@ -1865,48 +1412,86 @@ * Calls a method of a Node-style object that accepts a Node-style | ||
*/ | ||
Q.nsend = // XXX Based on Mark Miller's proposed "send" | ||
Q.nmcall = // XXX Based on "Redsandro's" proposal | ||
Q.ninvoke = function (object, name /*...args*/) { | ||
var nodeArgs = array_slice(arguments, 2); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
Q(object).dispatch("post", [name, nodeArgs]).fail(deferred.reject); | ||
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] = makeNodebackResolver(deferred.resolve); | ||
Q(object).dispatch("invoke", [name, args]).catch(deferred.reject); | ||
return deferred.promise; | ||
}; | ||
Promise.prototype.nsend = // XXX Based on Mark Miller's proposed "send" | ||
Promise.prototype.nmcall = // XXX Based on "Redsandro's" proposal | ||
Promise.prototype.ninvoke = function (name /*...args*/) { | ||
var nodeArgs = array_slice(arguments, 1); | ||
var deferred = defer(); | ||
nodeArgs.push(deferred.makeNodeResolver()); | ||
this.dispatch("post", [name, nodeArgs]).fail(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] = makeNodebackResolver(deferred.resolve, pattern); | ||
Q(callback).apply(this, args).catch(deferred.reject); | ||
return deferred.promise; | ||
}; | ||
}; | ||
/** | ||
* If a function would like to support both Node continuation-passing-style and | ||
* promise-returning-style, it can end its internal promise chain with | ||
* `nodeify(nodeback)`, forwarding the optional nodeback argument. If the user | ||
* elects to use a nodeback, the result will be sent there. If they do not | ||
* pass a nodeback, they will receive the result promise. | ||
* @param object a result (or a promise for a result) | ||
* @param {Function} nodeback a Node.js-style callback | ||
* @returns either the promise or nothing | ||
* Creates a Node.js-style callback that will resolve or reject the deferred | ||
* promise. | ||
* TODO | ||
* @returns a nodeback | ||
* @private | ||
*/ | ||
Q.nodeify = nodeify; | ||
function nodeify(object, nodeback) { | ||
return Q(object).nodeify(nodeback); | ||
function makeNodebackResolver(resolve, names) { | ||
if (names === 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 (names) { | ||
return function namedArgumentNodebackToResolver(error) { | ||
if (error) { | ||
resolve(Q_reject(error)); | ||
} else { | ||
var value = {}; | ||
for (var index in names) { | ||
value[names[index]] = arguments[index + 1]; | ||
} | ||
resolve(value); | ||
} | ||
}; | ||
} else { | ||
return function nodebackToResolver(error, value) { | ||
if (error) { | ||
resolve(Q_reject(error)); | ||
} else { | ||
resolve(value); | ||
} | ||
}; | ||
} | ||
} | ||
Promise.prototype.nodeify = function (nodeback) { | ||
/** | ||
* TODO | ||
*/ | ||
Promise.prototype.nodeify = function Promise_nodeify(nodeback) { | ||
if (nodeback) { | ||
this.then(function (value) { | ||
nextTick(function () { | ||
nodeback(null, value); | ||
}); | ||
}, function (error) { | ||
nextTick(function () { | ||
nodeback(error); | ||
}); | ||
}); | ||
this.done(function (value) { | ||
nodeback(null, value); | ||
}, nodeback); | ||
} else { | ||
@@ -1917,7 +1502,260 @@ 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(makeNodebackResolver(deferred.resolve)); | ||
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(makeNodebackResolver(deferred.resolve)); | ||
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(makeNodebackResolver(deferred.resolve)); | ||
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(makeNodebackResolver(deferred.resolve)); | ||
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.makeNodeResolver = deprecate(makeNodebackResolver, "makeNodeResolver"); | ||
Promise.prototype.ninvoke = deprecate(function (name) { | ||
var args = new Array(arguments.length - 1); | ||
for (var index = 1; index < arguments.length; index++) { | ||
args[index - 1] = arguments[index]; | ||
} | ||
return Q.npost(this, name, args); | ||
}, "ninvoke", "Q.ninvoke"); | ||
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(); | ||
return Q; | ||
}); |
42
queue.js
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
@@ -6,31 +7,20 @@ var Q = require("./q"); | ||
function Queue() { | ||
if (!(this instanceof Queue)) { | ||
return new Queue(); | ||
} | ||
var ends = Q.defer(); | ||
var closed = Q.defer(); | ||
return { | ||
put: function (value) { | ||
var next = Q.defer(); | ||
ends.resolve({ | ||
head: value, | ||
tail: next.promise | ||
}); | ||
ends.resolve = next.resolve; | ||
}, | ||
get: function () { | ||
var result = ends.promise.get("head"); | ||
ends.promise = ends.promise.get("tail"); | ||
return result.fail(function (error) { | ||
closed.resolve(error); | ||
throw error; | ||
}); | ||
}, | ||
closed: closed.promise, | ||
close: function (error) { | ||
error = error || new Error("Can't get value from closed queue"); | ||
var end = {head: Q.reject(error)}; | ||
end.tail = end; | ||
ends.resolve(end); | ||
return closed.promise; | ||
} | ||
this.put = function (value) { | ||
var next = Q.defer(); | ||
ends.resolve({ | ||
head: value, | ||
tail: next.promise | ||
}); | ||
ends.resolve = next.resolve; | ||
}; | ||
this.get = function () { | ||
var result = ends.promise.get("head"); | ||
ends.promise = ends.promise.get("tail"); | ||
return result; | ||
}; | ||
} | ||
232
README.md
@@ -8,9 +8,15 @@ [![Build Status](https://secure.travis-ci.org/kriskowal/q.png?branch=master)](http://travis-ci.org/kriskowal/q) | ||
*This is Q version 1, from the `v1` branch in Git. This documentation applies to | ||
the latest of both the version 1 and version 0.9 release trains. These releases | ||
are stable. There will be no further releases of 0.9 after 0.9.7 which is nearly | ||
equivalent to version 1.0.0. All further releases of `q@~1.0` will be backward | ||
compatible. The version 2 release train introduces significant but | ||
backward-incompatible changes and is experimental at this time.* | ||
*:warning: This is Q version 2 and is experimental at this time. If you install | ||
the latest Q from `npm`, you will get the latest from the [version 1][v1] | ||
release train. You will get the lastet of version 2 if you use `npm install | ||
q@~2`. Consult [CHANGES.md][] for details on what has changed* | ||
*Among the significant differences in version 2, the source is CommonJS only and | ||
versions suitable for use with AMD and plain `<script>` tags are built and | ||
published for [download][] with each release.* | ||
[v1]: https://github.com/kriskowal/q/tree/v1 | ||
[download]: http://q-releases.s3-website-us-west-1.amazonaws.com/ | ||
[CHANGES.md]: https://github.com/kriskowal/q/blob/v2/CHANGES.md | ||
If a function cannot return a value or throw an exception without | ||
@@ -24,52 +30,3 @@ blocking, it can return a promise instead. A promise is an object | ||
On the first pass, promises can mitigate the “[Pyramid of | ||
Doom][POD]”: the situation where code marches to the right faster | ||
than it marches forward. | ||
[POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/ | ||
```javascript | ||
step1(function (value1) { | ||
step2(value1, function(value2) { | ||
step3(value2, function(value3) { | ||
step4(value3, function(value4) { | ||
// Do something with value4 | ||
}); | ||
}); | ||
}); | ||
}); | ||
``` | ||
With a promise library, you can flatten the pyramid. | ||
```javascript | ||
Q.fcall(promisedStep1) | ||
.then(promisedStep2) | ||
.then(promisedStep3) | ||
.then(promisedStep4) | ||
.then(function (value4) { | ||
// Do something with value4 | ||
}) | ||
.catch(function (error) { | ||
// Handle any error from all above steps | ||
}) | ||
.done(); | ||
``` | ||
With this approach, you also get implicit error propagation, just like `try`, | ||
`catch`, and `finally`. An error in `promisedStep1` will flow all the way to | ||
the `catch` function, where it’s caught and handled. (Here `promisedStepN` is | ||
a version of `stepN` that returns a promise.) | ||
The callback approach is called an “inversion of control”. | ||
A function that accepts a callback instead of a return value | ||
is saying, “Don’t call me, I’ll call you.”. Promises | ||
[un-invert][IOC] the inversion, cleanly separating the input | ||
arguments from control flow arguments. This simplifies the | ||
use and creation of API’s, particularly variadic, | ||
rest and spread arguments. | ||
[IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript | ||
## Getting Started | ||
@@ -80,6 +37,8 @@ | ||
- A ``<script>`` tag (creating a ``Q`` global variable): ~2.5 KB minified and | ||
gzipped. | ||
gzipped. Download the latest of [version | ||
0.9](https://raw.github.com/kriskowal/q/v0.9/q.js) | ||
- A Node.js and CommonJS module, available in [npm](https://npmjs.org/) as | ||
the [q](https://npmjs.org/package/q) package | ||
- An AMD module | ||
- An AMD module. [Download version | ||
0.9](https://raw.github.com/kriskowal/q/v0.9/q.js) | ||
- A [component](https://github.com/component/component) as ``microjs/q`` | ||
@@ -118,2 +77,142 @@ - Using [bower](http://bower.io/) as ``q`` | ||
## Introduction | ||
There are many reasons to use promises. The first reward is that | ||
promises implicitly propagate errors and values downstream. Consider | ||
this synchronous solution to reading a file and parsing its content. | ||
```javascript | ||
var FS = require("fs"); | ||
var readJsonSync = function (path) { | ||
return JSON.parse(FS.readSync(path, "utf-8")); | ||
}; | ||
``` | ||
The asynchronous analog would ideally look and behave exactly the same | ||
*except* it would explicitly mark anywhere it might yield to other | ||
tasks, which is to say, between calling and returning, and reading and | ||
parsing. Control flow constructs like `return`, `throw`, `if`, `for`, | ||
`break` and `continue` would still work, except asynchronously. | ||
Exceptions, such as the `SyntaxError` that `JSON.parse` might throw, | ||
would propagate through the promise graph just as they do through the | ||
synchronous stack. Forbes Lindesay illustrates the way to this happy | ||
ideal in his presentation, [“Promises and Generators”][PAG]. | ||
[PAG]: http://pag.forbeslindesay.co.uk/ | ||
```javascript | ||
var FS = require("q-io/fs"); | ||
var readJsonPromise = Q.async(function *(path) { | ||
return JSON.parse(yield FS.read(path)); | ||
}); | ||
``` | ||
Explicitly marking yield points makes it possible for users to take | ||
advantage of the invariant that they can arrange for a consistent | ||
internal state between events, and be guaranteed that only they can | ||
alter their state during an event. Fibers and threads do not provide | ||
this guarantee, so programmers must work with a heightened sense of | ||
caution—their work may be interrupted and their state modified at any | ||
function call boundary for fibers, or at *any time at all* with threads. | ||
But even without generators, by using promises, we can at least get | ||
exceptions to implicitly propagate asynchronously with very little | ||
noise. | ||
```javascript | ||
var FS = require("q-io/fs"); | ||
function readJsonPromise(path) { | ||
return FS.read(path).then(JSON.parse); | ||
} | ||
``` | ||
Compare these solutions to the equivalent using bare callbacks. It must | ||
use an explicit `try` block to `catch` the exception that `JSON.parse` | ||
might throw and must manually forward all errors to the subscriber. It | ||
also must take care not to call the subscriber inside the try block, | ||
since this would catch errors thrown by `nodeback` and throw them back | ||
at `nodeback` in the catch block. In general, writing callback-based | ||
functions that handle errors robustly is difficult and error-prone, and | ||
even if you do it right, rather verbose. | ||
```javascript | ||
var FS = require("fs"); | ||
var readJsonWithNodebacks = function (path, nodeback) { | ||
FS.readFile(path, "utf-8", function (error, content) { | ||
var result; | ||
if (error) { | ||
return nodeback(error); | ||
} | ||
try { | ||
result = JSON.parse(result); | ||
} catch (error) { | ||
return nodeback(error); | ||
} | ||
nodeback(null, result); | ||
}); | ||
} | ||
``` | ||
The second reward for using promises is that they implicitly guarantee | ||
that interfaces you create will be strictly asynchronous. Oliver | ||
Steele’s [Minimizing Code Paths in Asynchronous Code][Steele] succinctly | ||
captures the issue and Isaac Schlueter’s more recent treatise, | ||
[Designing APIs for Asynchrony][Isaac], reframed the edict as “Do Not | ||
Release Zalgo”. | ||
[Steele]: http://blog.osteele.com/posts/2008/04/minimizing-code-paths-in-asychronous-code | ||
[Isaac]: http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony | ||
If you are using Q, you can cast any promise, even a [jQuery | ||
“promise”][jQuery], into a well-behaved promise that will not call event | ||
handlers until your event is done. | ||
[jQuery]: https://github.com/kriskowal/q/wiki/Coming-from-jQuery | ||
```javascript | ||
var x = 10; | ||
var part1 = Q($.ajax(...)) | ||
.then(function () { | ||
x = 20; | ||
}); | ||
var part2 = Q($.ajax(...)) | ||
.then(function () { | ||
x = 30; | ||
}); | ||
expect(x).toBe(10); // still, no matter what | ||
``` | ||
Using promises also preserves the signatures of synchronous functions. | ||
Continuation passing style is an “inversion of control”, where you pass | ||
control forward instead of getting it back when a function returns. | ||
Promises [un-invert][IOC] the inversion, cleanly separating the input | ||
arguments from control flow arguments. This simplifies the use and | ||
creation of API’s, particularly variadic, rest and spread arguments. | ||
[IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript | ||
Another point to using promises is that multiple subscribers can wait | ||
for a result, and new subscribers can be added even after the result has | ||
been published. Consider how much simpler it would be to wait for | ||
DOMContentLoaded with promises. No need to worry about whether the | ||
event has already passed. | ||
```javascript | ||
return document.ready.then(setup); | ||
``` | ||
Promises go on to be a useful primitive for capturing the “causal graph” | ||
of an asynchronous program, providing “long traces” that capture the | ||
stacks from all the events that led to an exception. Promises are also | ||
useful as proxies for objects in other processes, pipelining messages | ||
over any inter-process message channel. | ||
The point of promises is that they have scouted the way ahead and will | ||
help you avoid set-backs and dead-ends, from simple problems like | ||
synchronizing local work, to more advanced problems [like distributed | ||
robust secure escrow exchange][MarkM]. | ||
[MarkM]: http://scholar.google.com/citations?user=PuP2INoAAAAJ&hl=en&oi=ao | ||
## Tutorial | ||
@@ -301,3 +400,3 @@ | ||
will get called at the first sign of failure. That is, whichever of | ||
the recived promises fails first gets handled by the rejection handler. | ||
the received promises fails first gets handled by the rejection handler. | ||
@@ -366,4 +465,7 @@ ```javascript | ||
You can make this slightly more compact using `reduce`: | ||
You can make this slightly more compact using `reduce` (a | ||
[method][reduce] of arrays introduced in ECMAScript 5): | ||
[reduce]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce | ||
```javascript | ||
@@ -375,3 +477,3 @@ return funcs.reduce(function (soFar, f) { | ||
Or, you could use th ultra-compact version: | ||
Or, you could use the ultra-compact version: | ||
@@ -385,3 +487,3 @@ ```javascript | ||
One sometimes-unintuive aspect of promises is that if you throw an | ||
exception in the fulfillment handler, it will not be be caught by the error | ||
exception in the fulfillment handler, it will not be caught by the error | ||
handler. | ||
@@ -818,12 +920,6 @@ | ||
## Tests | ||
You can view the results of the Q test suite [in your browser][tests]! | ||
[tests]: https://rawgithub.com/kriskowal/q/v1/spec/q-spec.html | ||
## License | ||
Copyright 2009–2014 Kristopher Michael Kowal | ||
Copyright 2009–2013 Kristopher Michael Kowal | ||
MIT License (enclosed) | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
163807
19
3784
917
2
13
2
1
+ Addedasap@^1.0.0
+ Addedcollections@^2.0.0
+ Addedasap@1.0.0(transitive)
+ Addedcollections@2.0.3(transitive)
+ Addedmini-map@1.0.0(transitive)
+ Addedpop-arrayify@1.0.0(transitive)
+ Addedpop-clear@1.0.0(transitive)
+ Addedpop-clone@1.0.1(transitive)
+ Addedpop-compare@1.0.0(transitive)
+ Addedpop-equals@1.0.0(transitive)
+ Addedpop-has@1.0.0(transitive)
+ Addedpop-hash@1.0.1(transitive)
+ Addedpop-iterate@1.0.1(transitive)
+ Addedpop-observe@2.0.2(transitive)
+ Addedpop-swap@1.0.0(transitive)
+ Addedpop-zip@1.0.0(transitive)
+ Addedregexp-escape@0.0.1(transitive)
+ Addedweak-map@1.0.8(transitive)