Skip to content

Commit

Permalink
Merge pull request kriskowal#286
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jun 4, 2013
2 parents 4e2ae5d + 1eefd58 commit 1657311
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 125 deletions.
141 changes: 94 additions & 47 deletions q.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,17 @@ function captureLine() {
}
}

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 callback.apply(callback, arguments);
};
}

// end of shims
// beginning of real work

Expand Down Expand Up @@ -452,15 +463,23 @@ function defer() {
}
};

promise.valueOf = function () {
// XXX deprecated
promise.valueOf = deprecate(function () {
if (messages) {
return promise;
}
var nearer = valueOf(resolvedPromise);
if (isPromise(nearer)) {
resolvedPromise = nearer; // shorten chain
var nearerValue = nearer(resolvedPromise);
if (isPromise(nearerValue)) {
resolvedPromise = nearerValue; // shorten chain
}
return nearerValue;
}, "valueOf", "inspect");

promise.inspect = function () {
if (!resolvedPromise) {
return { state: "pending" };
}
return nearer;
return resolvedPromise.inspect();
};

if (Q.longStackJumpLimit > 0 && hasStacks) {
Expand Down Expand Up @@ -584,7 +603,7 @@ function promise(resolver) {
* bought and sold.
*/
Q.makePromise = makePromise;
function makePromise(descriptor, fallback, valueOf, exception, isException) {
function makePromise(descriptor, fallback, inspect) {
if (fallback === void 0) {
fallback = function (op) {
return reject(new Error(
Expand All @@ -611,12 +630,23 @@ function makePromise(descriptor, fallback, valueOf, exception, isException) {
}
};

if (valueOf) {
promise.valueOf = valueOf;
}
promise.inspect = inspect;

// XXX deprecated `valueOf` and `exception` support
if (inspect) {
var inspected = inspect();
if (inspected.state === "rejected") {
promise.exception = inspected.reason;
}

if (isException) {
promise.exception = exception;
promise.valueOf = deprecate(function () {
var inspected = inspect();
if (inspected.state === "pending" ||
inspected.state === "rejected") {
return promise;
}
return inspected.value;
});
}

return promise;
Expand Down Expand Up @@ -680,10 +710,15 @@ makePromise.prototype.toString = function () {
* @param object
* @returns most resolved (nearest) form of the object
*/
Q.nearer = valueOf;
function valueOf(value) {

// XXX should we re-do this?
Q.nearer = nearer;
function nearer(value) {
if (isPromise(value)) {
return value.valueOf();
var inspected = value.inspect();
if (inspected.state === "fulfilled") {
return inspected.value;
}
}
return value;
}
Expand All @@ -708,7 +743,7 @@ function isPromiseAlike(object) {
*/
Q.isPending = isPending;
function isPending(object) {
return !isFulfilled(object) && !isRejected(object);
return isPromise(object) && object.inspect().state === "pending";
}

/**
Expand All @@ -717,16 +752,15 @@ function isPending(object) {
*/
Q.isFulfilled = isFulfilled;
function isFulfilled(object) {
return !isPromiseAlike(valueOf(object));
return !isPromise(object) || object.inspect().state === "fulfilled";
}

/**
* @returns whether the given object is a rejected promise.
*/
Q.isRejected = isRejected;
function isRejected(object) {
object = valueOf(object);
return isPromise(object) && "exception" in object;
return isPromise(object) && object.inspect().state === "rejected";
}

//// BEGIN UNHANDLED REJECTION TRACKING
Expand Down Expand Up @@ -838,9 +872,9 @@ function reject(reason) {
}
}, function fallback() {
return this;
}, function valueOf() {
return this;
}, reason, true);
}, function inspect() {
return { state: "rejected", reason: reason };
});

// Note that the reason has not been handled.
trackRejection(rejection, reason);
Expand All @@ -853,37 +887,37 @@ function reject(reason) {
* @param value immediate reference
*/
Q.fulfill = fulfill;
function fulfill(object) {
function fulfill(value) {
return makePromise({
"when": function () {
return object;
return value;
},
"get": function (name) {
return object[name];
return value[name];
},
"set": function (name, value) {
object[name] = value;
"set": function (name, rhs) {
value[name] = rhs;
},
"delete": function (name) {
delete object[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 object.apply(void 0, args);
return value.apply(void 0, args);
} else {
return object[name].apply(object, args);
return value[name].apply(value, args);
}
},
"apply": function (thisP, args) {
return object.apply(thisP, args);
return value.apply(thisP, args);
},
"keys": function () {
return object_keys(object);
return object_keys(value);
}
}, void 0, function valueOf() {
return object;
}, void 0, function inspect() {
return { state: "fulfilled", value: value };
});
}

Expand All @@ -900,16 +934,8 @@ function resolve(value) {
if (isPromise(value)) {
return value;
}
// In order to break infinite recursion or loops between `then` and
// `resolve`, it is necessary to attempt to extract fulfilled values
// out of foreign promise implementations before attempting to wrap
// them as unresolved promises. It is my hope that other
// implementations will implement `valueOf` to synchronously extract
// the fulfillment value from their fulfilled promises. If the
// other promise library does not implement `valueOf`, the
// implementations on primordial prototypes are harmless.
value = valueOf(value);
// assimilate thenables, CommonJS/Promises/A+

// assimilate thenables
if (isPromiseAlike(value)) {
return coerce(value);
} else {
Expand Down Expand Up @@ -950,7 +976,7 @@ function master(object) {
}, function fallback(op, args) {
return dispatch(object, op, args);
}, function () {
return valueOf(object);
return resolve(object).inspect();
});
}

Expand Down Expand Up @@ -1325,8 +1351,10 @@ function all(promises) {
var countDown = 0;
var deferred = defer();
array_reduce(promises, function (undefined, promise, index) {
if (isFulfilled(promise)) {
promises[index] = valueOf(promise);
var snapshot;
if (isPromise(promise) &&
(snapshot = promise.inspect()).state === "fulfilled") {
promises[index] = snapshot.value;
} else {
++countDown;
when(promise, function (value) {
Expand All @@ -1353,7 +1381,7 @@ function all(promises) {
* (or values)
* @return a promise for an array of promises
*/
Q.allResolved = allResolved;
Q.allResolved = deprecate(allResolved, "allResolved", "allSettled");
function allResolved(promises) {
return when(promises, function (promises) {
promises = array_map(promises, resolve);
Expand All @@ -1365,6 +1393,25 @@ function allResolved(promises) {
});
}

Q.allSettled = allSettled;
function allSettled(values) {
return when(values, function (values) {
return all(array_map(values, function (value, i) {
return when(
value,
function (fulfillmentValue) {
values[i] = { state: "fulfilled", value: fulfillmentValue };
return values[i];
},
function (reason) {
values[i] = { state: "rejected", reason: reason };
return values[i];
}
);
})).thenResolve(values);
});
}

/**
* Captures the failure of a promise, giving an oportunity to recover
* with a callback. If the given promise is fulfilled, the returned
Expand Down
Loading

0 comments on commit 1657311

Please sign in to comment.