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