147 lines
4.5 KiB
JavaScript
147 lines
4.5 KiB
JavaScript
"use strict";
|
|
module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) {
|
|
var util = require("./util");
|
|
var CancellationError = Promise.CancellationError;
|
|
var errorObj = util.errorObj;
|
|
var catchFilter = require("./catch_filter")(NEXT_FILTER);
|
|
|
|
function PassThroughHandlerContext(promise, type, handler) {
|
|
this.promise = promise;
|
|
this.type = type;
|
|
this.handler = handler;
|
|
this.called = false;
|
|
this.cancelPromise = null;
|
|
}
|
|
|
|
PassThroughHandlerContext.prototype.isFinallyHandler = function() {
|
|
return this.type === 0;
|
|
};
|
|
|
|
function FinallyHandlerCancelReaction(finallyHandler) {
|
|
this.finallyHandler = finallyHandler;
|
|
}
|
|
|
|
FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
|
|
checkCancel(this.finallyHandler);
|
|
};
|
|
|
|
function checkCancel(ctx, reason) {
|
|
if (ctx.cancelPromise != null) {
|
|
if (arguments.length > 1) {
|
|
ctx.cancelPromise._reject(reason);
|
|
} else {
|
|
ctx.cancelPromise._cancel();
|
|
}
|
|
ctx.cancelPromise = null;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function succeed() {
|
|
return finallyHandler.call(this, this.promise._target()._settledValue());
|
|
}
|
|
function fail(reason) {
|
|
if (checkCancel(this, reason)) return;
|
|
errorObj.e = reason;
|
|
return errorObj;
|
|
}
|
|
function finallyHandler(reasonOrValue) {
|
|
var promise = this.promise;
|
|
var handler = this.handler;
|
|
|
|
if (!this.called) {
|
|
this.called = true;
|
|
var ret = this.isFinallyHandler()
|
|
? handler.call(promise._boundValue())
|
|
: handler.call(promise._boundValue(), reasonOrValue);
|
|
if (ret === NEXT_FILTER) {
|
|
return ret;
|
|
} else if (ret !== undefined) {
|
|
promise._setReturnedNonUndefined();
|
|
var maybePromise = tryConvertToPromise(ret, promise);
|
|
if (maybePromise instanceof Promise) {
|
|
if (this.cancelPromise != null) {
|
|
if (maybePromise._isCancelled()) {
|
|
var reason =
|
|
new CancellationError("late cancellation observer");
|
|
promise._attachExtraTrace(reason);
|
|
errorObj.e = reason;
|
|
return errorObj;
|
|
} else if (maybePromise.isPending()) {
|
|
maybePromise._attachCancellationCallback(
|
|
new FinallyHandlerCancelReaction(this));
|
|
}
|
|
}
|
|
return maybePromise._then(
|
|
succeed, fail, undefined, this, undefined);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (promise.isRejected()) {
|
|
checkCancel(this);
|
|
errorObj.e = reasonOrValue;
|
|
return errorObj;
|
|
} else {
|
|
checkCancel(this);
|
|
return reasonOrValue;
|
|
}
|
|
}
|
|
|
|
Promise.prototype._passThrough = function(handler, type, success, fail) {
|
|
if (typeof handler !== "function") return this.then();
|
|
return this._then(success,
|
|
fail,
|
|
undefined,
|
|
new PassThroughHandlerContext(this, type, handler),
|
|
undefined);
|
|
};
|
|
|
|
Promise.prototype.lastly =
|
|
Promise.prototype["finally"] = function (handler) {
|
|
return this._passThrough(handler,
|
|
0,
|
|
finallyHandler,
|
|
finallyHandler);
|
|
};
|
|
|
|
|
|
Promise.prototype.tap = function (handler) {
|
|
return this._passThrough(handler, 1, finallyHandler);
|
|
};
|
|
|
|
Promise.prototype.tapCatch = function (handlerOrPredicate) {
|
|
var len = arguments.length;
|
|
if(len === 1) {
|
|
return this._passThrough(handlerOrPredicate,
|
|
1,
|
|
undefined,
|
|
finallyHandler);
|
|
} else {
|
|
var catchInstances = new Array(len - 1),
|
|
j = 0, i;
|
|
for (i = 0; i < len - 1; ++i) {
|
|
var item = arguments[i];
|
|
if (util.isObject(item)) {
|
|
catchInstances[j++] = item;
|
|
} else {
|
|
return Promise.reject(new TypeError(
|
|
"tapCatch statement predicate: "
|
|
+ "expecting an object but got " + util.classString(item)
|
|
));
|
|
}
|
|
}
|
|
catchInstances.length = j;
|
|
var handler = arguments[i];
|
|
return this._passThrough(catchFilter(catchInstances, handler, this),
|
|
1,
|
|
undefined,
|
|
finallyHandler);
|
|
}
|
|
|
|
};
|
|
|
|
return PassThroughHandlerContext;
|
|
};
|