| "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; |
| }; |